/*
 * Binance Spot WebSocket API
 *
 * OpenAPI Specifications for the Binance Spot WebSocket API
 *
 * API documents:
 * - [Github web-socket-api documentation file](https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-api.md)
 * - [General API information for web-socket-api on website](https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api/general-api-information)
 *
 *
 * The version of the OpenAPI document: 1.0.0
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

#![allow(unused_imports)]
use anyhow::Context;
use async_trait::async_trait;
use derive_builder::Builder;
use rust_decimal::prelude::*;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{collections::BTreeMap, sync::Arc};

use crate::common::{
    errors::WebsocketError,
    models::{ParamBuildError, WebsocketApiResponse},
    utils::remove_empty_value,
    websocket::{WebsocketApi, WebsocketMessageSendOptions},
};
use crate::spot::websocket_api::models;

#[async_trait]
pub trait TradeApi: Send + Sync {
    async fn open_orders_cancel_all(
        &self,
        params: OpenOrdersCancelAllParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrdersCancelAllResponseResultInner>>>;
    async fn order_amend_keep_priority(
        &self,
        params: OrderAmendKeepPriorityParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderAmendKeepPriorityResponseResult>>>;
    async fn order_cancel(
        &self,
        params: OrderCancelParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderCancelResponseResult>>>;
    async fn order_cancel_replace(
        &self,
        params: OrderCancelReplaceParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderCancelReplaceResponseResult>>>;
    async fn order_list_cancel(
        &self,
        params: OrderListCancelParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListCancelResponseResult>>>;
    async fn order_list_place(
        &self,
        params: OrderListPlaceParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListPlaceResponseResult>>>;
    async fn order_list_place_oco(
        &self,
        params: OrderListPlaceOcoParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListPlaceOcoResponseResult>>>;
    async fn order_list_place_oto(
        &self,
        params: OrderListPlaceOtoParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListPlaceOtoResponseResult>>>;
    async fn order_list_place_otoco(
        &self,
        params: OrderListPlaceOtocoParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListPlaceOtocoResponseResult>>>;
    async fn order_place(
        &self,
        params: OrderPlaceParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderPlaceResponseResult>>>;
    async fn order_test(
        &self,
        params: OrderTestParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderTestResponseResult>>>;
    async fn sor_order_place(
        &self,
        params: SorOrderPlaceParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::SorOrderPlaceResponseResultInner>>>;
    async fn sor_order_test(
        &self,
        params: SorOrderTestParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::SorOrderTestResponseResult>>>;
}

#[derive(Clone)]
pub struct TradeApiClient {
    websocket_api_base: Arc<WebsocketApi>,
}

impl TradeApiClient {
    pub fn new(websocket_api_base: Arc<WebsocketApi>) -> Self {
        Self { websocket_api_base }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelCancelRestrictionsEnum {
    #[serde(rename = "ONLY_NEW")]
    OnlyNew,
    #[serde(rename = "NEW")]
    New,
    #[serde(rename = "ONLY_PARTIALLY_FILLED")]
    OnlyPartiallyFilled,
    #[serde(rename = "PARTIALLY_FILLED")]
    PartiallyFilled,
}

impl OrderCancelCancelRestrictionsEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::OnlyNew => "ONLY_NEW",
            Self::New => "NEW",
            Self::OnlyPartiallyFilled => "ONLY_PARTIALLY_FILLED",
            Self::PartiallyFilled => "PARTIALLY_FILLED",
        }
    }
}

impl std::str::FromStr for OrderCancelCancelRestrictionsEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ONLY_NEW" => Ok(Self::OnlyNew),
            "NEW" => Ok(Self::New),
            "ONLY_PARTIALLY_FILLED" => Ok(Self::OnlyPartiallyFilled),
            "PARTIALLY_FILLED" => Ok(Self::PartiallyFilled),
            other => Err(format!("invalid OrderCancelCancelRestrictionsEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplaceCancelReplaceModeEnum {
    #[serde(rename = "STOP_ON_FAILURE")]
    StopOnFailure,
    #[serde(rename = "ALLOW_FAILURE")]
    AllowFailure,
}

impl OrderCancelReplaceCancelReplaceModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::StopOnFailure => "STOP_ON_FAILURE",
            Self::AllowFailure => "ALLOW_FAILURE",
        }
    }
}

impl std::str::FromStr for OrderCancelReplaceCancelReplaceModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "STOP_ON_FAILURE" => Ok(Self::StopOnFailure),
            "ALLOW_FAILURE" => Ok(Self::AllowFailure),
            other => {
                Err(format!("invalid OrderCancelReplaceCancelReplaceModeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplaceSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl OrderCancelReplaceSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for OrderCancelReplaceSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid OrderCancelReplaceSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplaceTypeEnum {
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderCancelReplaceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
            Self::StopLoss => "STOP_LOSS",
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
            Self::LimitMaker => "LIMIT_MAKER",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderCancelReplaceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            "STOP_LOSS" => Ok(Self::StopLoss),
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderCancelReplaceTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplaceTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderCancelReplaceTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderCancelReplaceTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderCancelReplaceTimeInForceEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplaceNewOrderRespTypeEnum {
    #[serde(rename = "ACK")]
    Ack,
    #[serde(rename = "RESULT")]
    Result,
    #[serde(rename = "FULL")]
    Full,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl OrderCancelReplaceNewOrderRespTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ack => "ACK",
            Self::Result => "RESULT",
            Self::Full => "FULL",
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for OrderCancelReplaceNewOrderRespTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ACK" => Ok(Self::Ack),
            "RESULT" => Ok(Self::Result),
            "FULL" => Ok(Self::Full),
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => {
                Err(format!("invalid OrderCancelReplaceNewOrderRespTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplaceSelfTradePreventionModeEnum {
    #[serde(rename = "NONE")]
    None,
    #[serde(rename = "EXPIRE_TAKER")]
    ExpireTaker,
    #[serde(rename = "EXPIRE_MAKER")]
    ExpireMaker,
    #[serde(rename = "EXPIRE_BOTH")]
    ExpireBoth,
    #[serde(rename = "DECREMENT")]
    Decrement,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderCancelReplaceSelfTradePreventionModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::None => "NONE",
            Self::ExpireTaker => "EXPIRE_TAKER",
            Self::ExpireMaker => "EXPIRE_MAKER",
            Self::ExpireBoth => "EXPIRE_BOTH",
            Self::Decrement => "DECREMENT",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderCancelReplaceSelfTradePreventionModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NONE" => Ok(Self::None),
            "EXPIRE_TAKER" => Ok(Self::ExpireTaker),
            "EXPIRE_MAKER" => Ok(Self::ExpireMaker),
            "EXPIRE_BOTH" => Ok(Self::ExpireBoth),
            "DECREMENT" => Ok(Self::Decrement),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!(
                "invalid OrderCancelReplaceSelfTradePreventionModeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplaceCancelRestrictionsEnum {
    #[serde(rename = "ONLY_NEW")]
    OnlyNew,
    #[serde(rename = "NEW")]
    New,
    #[serde(rename = "ONLY_PARTIALLY_FILLED")]
    OnlyPartiallyFilled,
    #[serde(rename = "PARTIALLY_FILLED")]
    PartiallyFilled,
}

impl OrderCancelReplaceCancelRestrictionsEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::OnlyNew => "ONLY_NEW",
            Self::New => "NEW",
            Self::OnlyPartiallyFilled => "ONLY_PARTIALLY_FILLED",
            Self::PartiallyFilled => "PARTIALLY_FILLED",
        }
    }
}

impl std::str::FromStr for OrderCancelReplaceCancelRestrictionsEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ONLY_NEW" => Ok(Self::OnlyNew),
            "NEW" => Ok(Self::New),
            "ONLY_PARTIALLY_FILLED" => Ok(Self::OnlyPartiallyFilled),
            "PARTIALLY_FILLED" => Ok(Self::PartiallyFilled),
            other => Err(format!(
                "invalid OrderCancelReplaceCancelRestrictionsEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplaceOrderRateLimitExceededModeEnum {
    #[serde(rename = "DO_NOTHING")]
    DoNothing,
    #[serde(rename = "CANCEL_ONLY")]
    CancelOnly,
}

impl OrderCancelReplaceOrderRateLimitExceededModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::DoNothing => "DO_NOTHING",
            Self::CancelOnly => "CANCEL_ONLY",
        }
    }
}

impl std::str::FromStr for OrderCancelReplaceOrderRateLimitExceededModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "DO_NOTHING" => Ok(Self::DoNothing),
            "CANCEL_ONLY" => Ok(Self::CancelOnly),
            other => Err(format!(
                "invalid OrderCancelReplaceOrderRateLimitExceededModeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplacePegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderCancelReplacePegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderCancelReplacePegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderCancelReplacePegPriceTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderCancelReplacePegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderCancelReplacePegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderCancelReplacePegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderCancelReplacePegOffsetTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl OrderListPlaceSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid OrderListPlaceSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceStopLimitTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "FOK")]
    Fok,
    #[serde(rename = "IOC")]
    Ioc,
}

impl OrderListPlaceStopLimitTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Fok => "FOK",
            Self::Ioc => "IOC",
        }
    }
}

impl std::str::FromStr for OrderListPlaceStopLimitTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "FOK" => Ok(Self::Fok),
            "IOC" => Ok(Self::Ioc),
            other => {
                Err(format!("invalid OrderListPlaceStopLimitTimeInForceEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceNewOrderRespTypeEnum {
    #[serde(rename = "ACK")]
    Ack,
    #[serde(rename = "RESULT")]
    Result,
    #[serde(rename = "FULL")]
    Full,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl OrderListPlaceNewOrderRespTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ack => "ACK",
            Self::Result => "RESULT",
            Self::Full => "FULL",
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for OrderListPlaceNewOrderRespTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ACK" => Ok(Self::Ack),
            "RESULT" => Ok(Self::Result),
            "FULL" => Ok(Self::Full),
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => Err(format!("invalid OrderListPlaceNewOrderRespTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceSelfTradePreventionModeEnum {
    #[serde(rename = "NONE")]
    None,
    #[serde(rename = "EXPIRE_TAKER")]
    ExpireTaker,
    #[serde(rename = "EXPIRE_MAKER")]
    ExpireMaker,
    #[serde(rename = "EXPIRE_BOTH")]
    ExpireBoth,
    #[serde(rename = "DECREMENT")]
    Decrement,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderListPlaceSelfTradePreventionModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::None => "NONE",
            Self::ExpireTaker => "EXPIRE_TAKER",
            Self::ExpireMaker => "EXPIRE_MAKER",
            Self::ExpireBoth => "EXPIRE_BOTH",
            Self::Decrement => "DECREMENT",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderListPlaceSelfTradePreventionModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NONE" => Ok(Self::None),
            "EXPIRE_TAKER" => Ok(Self::ExpireTaker),
            "EXPIRE_MAKER" => Ok(Self::ExpireMaker),
            "EXPIRE_BOTH" => Ok(Self::ExpireBoth),
            "DECREMENT" => Ok(Self::Decrement),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!(
                "invalid OrderListPlaceSelfTradePreventionModeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl OrderListPlaceOcoSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid OrderListPlaceOcoSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoAboveTypeEnum {
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
}

impl OrderListPlaceOcoAboveTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::StopLoss => "STOP_LOSS",
            Self::LimitMaker => "LIMIT_MAKER",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoAboveTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "STOP_LOSS" => Ok(Self::StopLoss),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            other => Err(format!("invalid OrderListPlaceOcoAboveTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoBelowTypeEnum {
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
}

impl OrderListPlaceOcoBelowTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::StopLoss => "STOP_LOSS",
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoBelowTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "STOP_LOSS" => Ok(Self::StopLoss),
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            other => Err(format!("invalid OrderListPlaceOcoBelowTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoAbovePegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
}

impl OrderListPlaceOcoAbovePegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoAbovePegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            other => {
                Err(format!("invalid OrderListPlaceOcoAbovePegPriceTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoAbovePegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
}

impl OrderListPlaceOcoAbovePegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoAbovePegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            other => {
                Err(format!("invalid OrderListPlaceOcoAbovePegOffsetTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoBelowTimeInForceEnum {
    #[serde(rename = "belowType")]
    Belowtype,
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
}

impl OrderListPlaceOcoBelowTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Belowtype => "belowType",
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoBelowTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "belowType" => Ok(Self::Belowtype),
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            other => {
                Err(format!("invalid OrderListPlaceOcoBelowTimeInForceEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoBelowPegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
}

impl OrderListPlaceOcoBelowPegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoBelowPegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            other => {
                Err(format!("invalid OrderListPlaceOcoBelowPegPriceTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoBelowPegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
}

impl OrderListPlaceOcoBelowPegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoBelowPegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            other => {
                Err(format!("invalid OrderListPlaceOcoBelowPegOffsetTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoNewOrderRespTypeEnum {
    #[serde(rename = "ACK")]
    Ack,
    #[serde(rename = "RESULT")]
    Result,
    #[serde(rename = "FULL")]
    Full,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl OrderListPlaceOcoNewOrderRespTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ack => "ACK",
            Self::Result => "RESULT",
            Self::Full => "FULL",
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoNewOrderRespTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ACK" => Ok(Self::Ack),
            "RESULT" => Ok(Self::Result),
            "FULL" => Ok(Self::Full),
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => {
                Err(format!("invalid OrderListPlaceOcoNewOrderRespTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOcoSelfTradePreventionModeEnum {
    #[serde(rename = "NONE")]
    None,
    #[serde(rename = "EXPIRE_TAKER")]
    ExpireTaker,
    #[serde(rename = "EXPIRE_MAKER")]
    ExpireMaker,
    #[serde(rename = "EXPIRE_BOTH")]
    ExpireBoth,
    #[serde(rename = "DECREMENT")]
    Decrement,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderListPlaceOcoSelfTradePreventionModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::None => "NONE",
            Self::ExpireTaker => "EXPIRE_TAKER",
            Self::ExpireMaker => "EXPIRE_MAKER",
            Self::ExpireBoth => "EXPIRE_BOTH",
            Self::Decrement => "DECREMENT",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOcoSelfTradePreventionModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NONE" => Ok(Self::None),
            "EXPIRE_TAKER" => Ok(Self::ExpireTaker),
            "EXPIRE_MAKER" => Ok(Self::ExpireMaker),
            "EXPIRE_BOTH" => Ok(Self::ExpireBoth),
            "DECREMENT" => Ok(Self::Decrement),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!(
                "invalid OrderListPlaceOcoSelfTradePreventionModeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoWorkingTypeEnum {
    #[serde(rename = "LIMIT")]
    Limit,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
}

impl OrderListPlaceOtoWorkingTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Limit => "LIMIT",
            Self::LimitMaker => "LIMIT_MAKER",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoWorkingTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "LIMIT" => Ok(Self::Limit),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            other => Err(format!("invalid OrderListPlaceOtoWorkingTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoWorkingSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl OrderListPlaceOtoWorkingSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoWorkingSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid OrderListPlaceOtoWorkingSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoPendingTypeEnum {
    #[serde(rename = "LIMIT")]
    Limit,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
}

impl OrderListPlaceOtoPendingTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Limit => "LIMIT",
            Self::Market => "MARKET",
            Self::StopLoss => "STOP_LOSS",
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
            Self::LimitMaker => "LIMIT_MAKER",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoPendingTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "LIMIT" => Ok(Self::Limit),
            "MARKET" => Ok(Self::Market),
            "STOP_LOSS" => Ok(Self::StopLoss),
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            other => Err(format!("invalid OrderListPlaceOtoPendingTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoPendingSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl OrderListPlaceOtoPendingSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoPendingSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid OrderListPlaceOtoPendingSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoNewOrderRespTypeEnum {
    #[serde(rename = "ACK")]
    Ack,
    #[serde(rename = "RESULT")]
    Result,
    #[serde(rename = "FULL")]
    Full,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl OrderListPlaceOtoNewOrderRespTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ack => "ACK",
            Self::Result => "RESULT",
            Self::Full => "FULL",
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoNewOrderRespTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ACK" => Ok(Self::Ack),
            "RESULT" => Ok(Self::Result),
            "FULL" => Ok(Self::Full),
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => {
                Err(format!("invalid OrderListPlaceOtoNewOrderRespTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoSelfTradePreventionModeEnum {
    #[serde(rename = "NONE")]
    None,
    #[serde(rename = "EXPIRE_TAKER")]
    ExpireTaker,
    #[serde(rename = "EXPIRE_MAKER")]
    ExpireMaker,
    #[serde(rename = "EXPIRE_BOTH")]
    ExpireBoth,
    #[serde(rename = "DECREMENT")]
    Decrement,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderListPlaceOtoSelfTradePreventionModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::None => "NONE",
            Self::ExpireTaker => "EXPIRE_TAKER",
            Self::ExpireMaker => "EXPIRE_MAKER",
            Self::ExpireBoth => "EXPIRE_BOTH",
            Self::Decrement => "DECREMENT",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoSelfTradePreventionModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NONE" => Ok(Self::None),
            "EXPIRE_TAKER" => Ok(Self::ExpireTaker),
            "EXPIRE_MAKER" => Ok(Self::ExpireMaker),
            "EXPIRE_BOTH" => Ok(Self::ExpireBoth),
            "DECREMENT" => Ok(Self::Decrement),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!(
                "invalid OrderListPlaceOtoSelfTradePreventionModeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoWorkingTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
}

impl OrderListPlaceOtoWorkingTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoWorkingTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            other => {
                Err(format!("invalid OrderListPlaceOtoWorkingTimeInForceEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoWorkingPegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
}

impl OrderListPlaceOtoWorkingPegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoWorkingPegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            other => Err(format!(
                "invalid OrderListPlaceOtoWorkingPegPriceTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoWorkingPegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
}

impl OrderListPlaceOtoWorkingPegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoWorkingPegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            other => Err(format!(
                "invalid OrderListPlaceOtoWorkingPegOffsetTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoPendingTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
}

impl OrderListPlaceOtoPendingTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoPendingTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            other => {
                Err(format!("invalid OrderListPlaceOtoPendingTimeInForceEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoPendingPegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
}

impl OrderListPlaceOtoPendingPegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoPendingPegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            other => Err(format!(
                "invalid OrderListPlaceOtoPendingPegOffsetTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtoPendingPegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
}

impl OrderListPlaceOtoPendingPegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtoPendingPegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            other => Err(format!(
                "invalid OrderListPlaceOtoPendingPegPriceTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoWorkingTypeEnum {
    #[serde(rename = "LIMIT")]
    Limit,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
}

impl OrderListPlaceOtocoWorkingTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Limit => "LIMIT",
            Self::LimitMaker => "LIMIT_MAKER",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoWorkingTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "LIMIT" => Ok(Self::Limit),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            other => Err(format!("invalid OrderListPlaceOtocoWorkingTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoWorkingSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl OrderListPlaceOtocoWorkingSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoWorkingSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid OrderListPlaceOtocoWorkingSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoPendingSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl OrderListPlaceOtocoPendingSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoPendingSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid OrderListPlaceOtocoPendingSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoPendingAboveTypeEnum {
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
}

impl OrderListPlaceOtocoPendingAboveTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::StopLoss => "STOP_LOSS",
            Self::LimitMaker => "LIMIT_MAKER",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoPendingAboveTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "STOP_LOSS" => Ok(Self::StopLoss),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            other => {
                Err(format!("invalid OrderListPlaceOtocoPendingAboveTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoNewOrderRespTypeEnum {
    #[serde(rename = "ACK")]
    Ack,
    #[serde(rename = "RESULT")]
    Result,
    #[serde(rename = "FULL")]
    Full,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl OrderListPlaceOtocoNewOrderRespTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ack => "ACK",
            Self::Result => "RESULT",
            Self::Full => "FULL",
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoNewOrderRespTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ACK" => Ok(Self::Ack),
            "RESULT" => Ok(Self::Result),
            "FULL" => Ok(Self::Full),
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => {
                Err(format!("invalid OrderListPlaceOtocoNewOrderRespTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoSelfTradePreventionModeEnum {
    #[serde(rename = "NONE")]
    None,
    #[serde(rename = "EXPIRE_TAKER")]
    ExpireTaker,
    #[serde(rename = "EXPIRE_MAKER")]
    ExpireMaker,
    #[serde(rename = "EXPIRE_BOTH")]
    ExpireBoth,
    #[serde(rename = "DECREMENT")]
    Decrement,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderListPlaceOtocoSelfTradePreventionModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::None => "NONE",
            Self::ExpireTaker => "EXPIRE_TAKER",
            Self::ExpireMaker => "EXPIRE_MAKER",
            Self::ExpireBoth => "EXPIRE_BOTH",
            Self::Decrement => "DECREMENT",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoSelfTradePreventionModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NONE" => Ok(Self::None),
            "EXPIRE_TAKER" => Ok(Self::ExpireTaker),
            "EXPIRE_MAKER" => Ok(Self::ExpireMaker),
            "EXPIRE_BOTH" => Ok(Self::ExpireBoth),
            "DECREMENT" => Ok(Self::Decrement),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!(
                "invalid OrderListPlaceOtocoSelfTradePreventionModeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoWorkingTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
}

impl OrderListPlaceOtocoWorkingTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoWorkingTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            other => Err(format!(
                "invalid OrderListPlaceOtocoWorkingTimeInForceEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoWorkingPegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
}

impl OrderListPlaceOtocoWorkingPegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoWorkingPegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            other => Err(format!(
                "invalid OrderListPlaceOtocoWorkingPegPriceTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoWorkingPegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
}

impl OrderListPlaceOtocoWorkingPegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoWorkingPegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            other => Err(format!(
                "invalid OrderListPlaceOtocoWorkingPegOffsetTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoPendingAboveTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
}

impl OrderListPlaceOtocoPendingAboveTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoPendingAboveTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            other => Err(format!(
                "invalid OrderListPlaceOtocoPendingAboveTimeInForceEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoPendingAbovePegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
}

impl OrderListPlaceOtocoPendingAbovePegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoPendingAbovePegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            other => Err(format!(
                "invalid OrderListPlaceOtocoPendingAbovePegPriceTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoPendingAbovePegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
}

impl OrderListPlaceOtocoPendingAbovePegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoPendingAbovePegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            other => Err(format!(
                "invalid OrderListPlaceOtocoPendingAbovePegOffsetTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoPendingBelowTypeEnum {
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
}

impl OrderListPlaceOtocoPendingBelowTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::StopLoss => "STOP_LOSS",
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoPendingBelowTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "STOP_LOSS" => Ok(Self::StopLoss),
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            other => {
                Err(format!("invalid OrderListPlaceOtocoPendingBelowTypeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoPendingBelowTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
}

impl OrderListPlaceOtocoPendingBelowTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoPendingBelowTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            other => Err(format!(
                "invalid OrderListPlaceOtocoPendingBelowTimeInForceEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoPendingBelowPegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
}

impl OrderListPlaceOtocoPendingBelowPegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoPendingBelowPegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            other => Err(format!(
                "invalid OrderListPlaceOtocoPendingBelowPegPriceTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderListPlaceOtocoPendingBelowPegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
}

impl OrderListPlaceOtocoPendingBelowPegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
        }
    }
}

impl std::str::FromStr for OrderListPlaceOtocoPendingBelowPegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            other => Err(format!(
                "invalid OrderListPlaceOtocoPendingBelowPegOffsetTypeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderPlaceSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl OrderPlaceSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for OrderPlaceSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid OrderPlaceSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderPlaceTypeEnum {
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderPlaceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
            Self::StopLoss => "STOP_LOSS",
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
            Self::LimitMaker => "LIMIT_MAKER",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderPlaceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            "STOP_LOSS" => Ok(Self::StopLoss),
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderPlaceTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderPlaceTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderPlaceTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderPlaceTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderPlaceTimeInForceEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderPlaceNewOrderRespTypeEnum {
    #[serde(rename = "ACK")]
    Ack,
    #[serde(rename = "RESULT")]
    Result,
    #[serde(rename = "FULL")]
    Full,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl OrderPlaceNewOrderRespTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ack => "ACK",
            Self::Result => "RESULT",
            Self::Full => "FULL",
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for OrderPlaceNewOrderRespTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ACK" => Ok(Self::Ack),
            "RESULT" => Ok(Self::Result),
            "FULL" => Ok(Self::Full),
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => Err(format!("invalid OrderPlaceNewOrderRespTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderPlaceSelfTradePreventionModeEnum {
    #[serde(rename = "NONE")]
    None,
    #[serde(rename = "EXPIRE_TAKER")]
    ExpireTaker,
    #[serde(rename = "EXPIRE_MAKER")]
    ExpireMaker,
    #[serde(rename = "EXPIRE_BOTH")]
    ExpireBoth,
    #[serde(rename = "DECREMENT")]
    Decrement,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderPlaceSelfTradePreventionModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::None => "NONE",
            Self::ExpireTaker => "EXPIRE_TAKER",
            Self::ExpireMaker => "EXPIRE_MAKER",
            Self::ExpireBoth => "EXPIRE_BOTH",
            Self::Decrement => "DECREMENT",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderPlaceSelfTradePreventionModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NONE" => Ok(Self::None),
            "EXPIRE_TAKER" => Ok(Self::ExpireTaker),
            "EXPIRE_MAKER" => Ok(Self::ExpireMaker),
            "EXPIRE_BOTH" => Ok(Self::ExpireBoth),
            "DECREMENT" => Ok(Self::Decrement),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => {
                Err(format!("invalid OrderPlaceSelfTradePreventionModeEnum: {}", other).into())
            }
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderPlacePegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderPlacePegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderPlacePegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderPlacePegPriceTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderPlacePegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderPlacePegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderPlacePegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderPlacePegOffsetTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderTestSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl OrderTestSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for OrderTestSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid OrderTestSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderTestTypeEnum {
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderTestTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
            Self::StopLoss => "STOP_LOSS",
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
            Self::LimitMaker => "LIMIT_MAKER",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderTestTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            "STOP_LOSS" => Ok(Self::StopLoss),
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderTestTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderTestTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderTestTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderTestTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderTestTimeInForceEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderTestNewOrderRespTypeEnum {
    #[serde(rename = "ACK")]
    Ack,
    #[serde(rename = "RESULT")]
    Result,
    #[serde(rename = "FULL")]
    Full,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl OrderTestNewOrderRespTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ack => "ACK",
            Self::Result => "RESULT",
            Self::Full => "FULL",
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for OrderTestNewOrderRespTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ACK" => Ok(Self::Ack),
            "RESULT" => Ok(Self::Result),
            "FULL" => Ok(Self::Full),
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => Err(format!("invalid OrderTestNewOrderRespTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderTestSelfTradePreventionModeEnum {
    #[serde(rename = "NONE")]
    None,
    #[serde(rename = "EXPIRE_TAKER")]
    ExpireTaker,
    #[serde(rename = "EXPIRE_MAKER")]
    ExpireMaker,
    #[serde(rename = "EXPIRE_BOTH")]
    ExpireBoth,
    #[serde(rename = "DECREMENT")]
    Decrement,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderTestSelfTradePreventionModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::None => "NONE",
            Self::ExpireTaker => "EXPIRE_TAKER",
            Self::ExpireMaker => "EXPIRE_MAKER",
            Self::ExpireBoth => "EXPIRE_BOTH",
            Self::Decrement => "DECREMENT",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderTestSelfTradePreventionModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NONE" => Ok(Self::None),
            "EXPIRE_TAKER" => Ok(Self::ExpireTaker),
            "EXPIRE_MAKER" => Ok(Self::ExpireMaker),
            "EXPIRE_BOTH" => Ok(Self::ExpireBoth),
            "DECREMENT" => Ok(Self::Decrement),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderTestSelfTradePreventionModeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderTestPegPriceTypeEnum {
    #[serde(rename = "PRIMARY_PEG")]
    PrimaryPeg,
    #[serde(rename = "MARKET_PEG")]
    MarketPeg,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderTestPegPriceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PrimaryPeg => "PRIMARY_PEG",
            Self::MarketPeg => "MARKET_PEG",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderTestPegPriceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRIMARY_PEG" => Ok(Self::PrimaryPeg),
            "MARKET_PEG" => Ok(Self::MarketPeg),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderTestPegPriceTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderTestPegOffsetTypeEnum {
    #[serde(rename = "PRICE_LEVEL")]
    PriceLevel,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl OrderTestPegOffsetTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PriceLevel => "PRICE_LEVEL",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for OrderTestPegOffsetTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "PRICE_LEVEL" => Ok(Self::PriceLevel),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid OrderTestPegOffsetTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderPlaceSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl SorOrderPlaceSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for SorOrderPlaceSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid SorOrderPlaceSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderPlaceTypeEnum {
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl SorOrderPlaceTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
            Self::StopLoss => "STOP_LOSS",
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
            Self::LimitMaker => "LIMIT_MAKER",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for SorOrderPlaceTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            "STOP_LOSS" => Ok(Self::StopLoss),
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid SorOrderPlaceTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderPlaceTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl SorOrderPlaceTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for SorOrderPlaceTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid SorOrderPlaceTimeInForceEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderPlaceNewOrderRespTypeEnum {
    #[serde(rename = "ACK")]
    Ack,
    #[serde(rename = "RESULT")]
    Result,
    #[serde(rename = "FULL")]
    Full,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl SorOrderPlaceNewOrderRespTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ack => "ACK",
            Self::Result => "RESULT",
            Self::Full => "FULL",
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for SorOrderPlaceNewOrderRespTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ACK" => Ok(Self::Ack),
            "RESULT" => Ok(Self::Result),
            "FULL" => Ok(Self::Full),
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => Err(format!("invalid SorOrderPlaceNewOrderRespTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderPlaceSelfTradePreventionModeEnum {
    #[serde(rename = "NONE")]
    None,
    #[serde(rename = "EXPIRE_TAKER")]
    ExpireTaker,
    #[serde(rename = "EXPIRE_MAKER")]
    ExpireMaker,
    #[serde(rename = "EXPIRE_BOTH")]
    ExpireBoth,
    #[serde(rename = "DECREMENT")]
    Decrement,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl SorOrderPlaceSelfTradePreventionModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::None => "NONE",
            Self::ExpireTaker => "EXPIRE_TAKER",
            Self::ExpireMaker => "EXPIRE_MAKER",
            Self::ExpireBoth => "EXPIRE_BOTH",
            Self::Decrement => "DECREMENT",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for SorOrderPlaceSelfTradePreventionModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NONE" => Ok(Self::None),
            "EXPIRE_TAKER" => Ok(Self::ExpireTaker),
            "EXPIRE_MAKER" => Ok(Self::ExpireMaker),
            "EXPIRE_BOTH" => Ok(Self::ExpireBoth),
            "DECREMENT" => Ok(Self::Decrement),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!(
                "invalid SorOrderPlaceSelfTradePreventionModeEnum: {}",
                other
            )
            .into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderTestSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl SorOrderTestSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for SorOrderTestSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid SorOrderTestSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderTestTypeEnum {
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
    #[serde(rename = "STOP_LOSS")]
    StopLoss,
    #[serde(rename = "STOP_LOSS_LIMIT")]
    StopLossLimit,
    #[serde(rename = "TAKE_PROFIT")]
    TakeProfit,
    #[serde(rename = "TAKE_PROFIT_LIMIT")]
    TakeProfitLimit,
    #[serde(rename = "LIMIT_MAKER")]
    LimitMaker,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl SorOrderTestTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
            Self::StopLoss => "STOP_LOSS",
            Self::StopLossLimit => "STOP_LOSS_LIMIT",
            Self::TakeProfit => "TAKE_PROFIT",
            Self::TakeProfitLimit => "TAKE_PROFIT_LIMIT",
            Self::LimitMaker => "LIMIT_MAKER",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for SorOrderTestTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            "STOP_LOSS" => Ok(Self::StopLoss),
            "STOP_LOSS_LIMIT" => Ok(Self::StopLossLimit),
            "TAKE_PROFIT" => Ok(Self::TakeProfit),
            "TAKE_PROFIT_LIMIT" => Ok(Self::TakeProfitLimit),
            "LIMIT_MAKER" => Ok(Self::LimitMaker),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid SorOrderTestTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderTestTimeInForceEnum {
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "IOC")]
    Ioc,
    #[serde(rename = "FOK")]
    Fok,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl SorOrderTestTimeInForceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Gtc => "GTC",
            Self::Ioc => "IOC",
            Self::Fok => "FOK",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for SorOrderTestTimeInForceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GTC" => Ok(Self::Gtc),
            "IOC" => Ok(Self::Ioc),
            "FOK" => Ok(Self::Fok),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => Err(format!("invalid SorOrderTestTimeInForceEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderTestNewOrderRespTypeEnum {
    #[serde(rename = "ACK")]
    Ack,
    #[serde(rename = "RESULT")]
    Result,
    #[serde(rename = "FULL")]
    Full,
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl SorOrderTestNewOrderRespTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ack => "ACK",
            Self::Result => "RESULT",
            Self::Full => "FULL",
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for SorOrderTestNewOrderRespTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ACK" => Ok(Self::Ack),
            "RESULT" => Ok(Self::Result),
            "FULL" => Ok(Self::Full),
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => Err(format!("invalid SorOrderTestNewOrderRespTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SorOrderTestSelfTradePreventionModeEnum {
    #[serde(rename = "NONE")]
    None,
    #[serde(rename = "EXPIRE_TAKER")]
    ExpireTaker,
    #[serde(rename = "EXPIRE_MAKER")]
    ExpireMaker,
    #[serde(rename = "EXPIRE_BOTH")]
    ExpireBoth,
    #[serde(rename = "DECREMENT")]
    Decrement,
    #[serde(rename = "NON_REPRESENTABLE")]
    NonRepresentable,
}

impl SorOrderTestSelfTradePreventionModeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::None => "NONE",
            Self::ExpireTaker => "EXPIRE_TAKER",
            Self::ExpireMaker => "EXPIRE_MAKER",
            Self::ExpireBoth => "EXPIRE_BOTH",
            Self::Decrement => "DECREMENT",
            Self::NonRepresentable => "NON_REPRESENTABLE",
        }
    }
}

impl std::str::FromStr for SorOrderTestSelfTradePreventionModeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NONE" => Ok(Self::None),
            "EXPIRE_TAKER" => Ok(Self::ExpireTaker),
            "EXPIRE_MAKER" => Ok(Self::ExpireMaker),
            "EXPIRE_BOTH" => Ok(Self::ExpireBoth),
            "DECREMENT" => Ok(Self::Decrement),
            "NON_REPRESENTABLE" => Ok(Self::NonRepresentable),
            other => {
                Err(format!("invalid SorOrderTestSelfTradePreventionModeEnum: {}", other).into())
            }
        }
    }
}

/// Request parameters for the [`open_orders_cancel_all`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`open_orders_cancel_all`](#method.open_orders_cancel_all).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OpenOrdersCancelAllParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OpenOrdersCancelAllParams {
    /// Create a builder for [`open_orders_cancel_all`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    ///
    #[must_use]
    pub fn builder(symbol: String) -> OpenOrdersCancelAllParamsBuilder {
        OpenOrdersCancelAllParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`order_amend_keep_priority`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_amend_keep_priority`](#method.order_amend_keep_priority).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderAmendKeepPriorityParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// `newQty` must be greater than 0 and less than the order's quantity.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub new_qty: rust_decimal::Decimal,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_id: Option<i64>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub orig_client_order_id: Option<String>,
    /// The new client order ID for the order after being amended. <br> If not sent, one will be randomly generated. <br> It is possible to reuse the current clientOrderId by sending it as the `newClientOrderId`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_client_order_id: Option<String>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderAmendKeepPriorityParams {
    /// Create a builder for [`order_amend_keep_priority`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `new_qty` — `newQty` must be greater than 0 and less than the order's quantity.
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        new_qty: rust_decimal::Decimal,
    ) -> OrderAmendKeepPriorityParamsBuilder {
        OrderAmendKeepPriorityParamsBuilder::default()
            .symbol(symbol)
            .new_qty(new_qty)
    }
}
/// Request parameters for the [`order_cancel`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_cancel`](#method.order_cancel).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderCancelParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_id: Option<i64>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub orig_client_order_id: Option<String>,
    /// The new client order ID for the order after being amended. <br> If not sent, one will be randomly generated. <br> It is possible to reuse the current clientOrderId by sending it as the `newClientOrderId`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_client_order_id: Option<String>,
    ///
    /// The `cancel_restrictions` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub cancel_restrictions: Option<OrderCancelCancelRestrictionsEnum>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderCancelParams {
    /// Create a builder for [`order_cancel`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    ///
    #[must_use]
    pub fn builder(symbol: String) -> OrderCancelParamsBuilder {
        OrderCancelParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`order_cancel_replace`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_cancel_replace`](#method.order_cancel_replace).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderCancelReplaceParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `cancel_replace_mode` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub cancel_replace_mode: OrderCancelReplaceCancelReplaceModeEnum,
    ///
    /// The `side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub side: OrderCancelReplaceSideEnum,
    ///
    /// The `r#type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub r#type: OrderCancelReplaceTypeEnum,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// Cancel order by orderId
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub cancel_order_id: Option<i64>,
    ///
    /// The `cancel_orig_client_order_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub cancel_orig_client_order_id: Option<String>,
    /// New ID for the canceled order. Automatically generated if not sent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub cancel_new_client_order_id: Option<String>,
    ///
    /// The `time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub time_in_force: Option<OrderCancelReplaceTimeInForceEnum>,
    ///
    /// The `price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub price: Option<rust_decimal::Decimal>,
    ///
    /// The `quantity` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub quantity: Option<rust_decimal::Decimal>,
    ///
    /// The `quote_order_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub quote_order_qty: Option<rust_decimal::Decimal>,
    /// The new client order ID for the order after being amended. <br> If not sent, one will be randomly generated. <br> It is possible to reuse the current clientOrderId by sending it as the `newClientOrderId`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_client_order_id: Option<String>,
    ///
    /// The `new_order_resp_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_order_resp_type: Option<OrderCancelReplaceNewOrderRespTypeEnum>,
    ///
    /// The `stop_price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_price: Option<rust_decimal::Decimal>,
    /// See Trailing Stop order FAQ
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub trailing_delta: Option<rust_decimal::Decimal>,
    ///
    /// The `iceberg_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub iceberg_qty: Option<rust_decimal::Decimal>,
    /// Arbitrary numeric value identifying the order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the order strategy.
    /// Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_type: Option<i32>,
    ///
    /// The `self_trade_prevention_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub self_trade_prevention_mode: Option<OrderCancelReplaceSelfTradePreventionModeEnum>,
    ///
    /// The `cancel_restrictions` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub cancel_restrictions: Option<OrderCancelReplaceCancelRestrictionsEnum>,
    ///
    /// The `order_rate_limit_exceeded_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_rate_limit_exceeded_mode: Option<OrderCancelReplaceOrderRateLimitExceededModeEnum>,
    ///
    /// The `peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub peg_price_type: Option<OrderCancelReplacePegPriceTypeEnum>,
    /// Price level to peg the price to (max: 100)
    /// See Pegged Orders
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub peg_offset_value: Option<i32>,
    ///
    /// The `peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub peg_offset_type: Option<OrderCancelReplacePegOffsetTypeEnum>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderCancelReplaceParams {
    /// Create a builder for [`order_cancel_replace`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `cancel_replace_mode` — String
    /// * `side` — String
    /// * `r#type` — String
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        cancel_replace_mode: OrderCancelReplaceCancelReplaceModeEnum,
        side: OrderCancelReplaceSideEnum,
        r#type: OrderCancelReplaceTypeEnum,
    ) -> OrderCancelReplaceParamsBuilder {
        OrderCancelReplaceParamsBuilder::default()
            .symbol(symbol)
            .cancel_replace_mode(cancel_replace_mode)
            .side(side)
            .r#type(r#type)
    }
}
/// Request parameters for the [`order_list_cancel`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_list_cancel`](#method.order_list_cancel).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderListCancelParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// Cancel order list by orderListId
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_list_id: Option<i32>,
    ///
    /// The `list_client_order_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub list_client_order_id: Option<String>,
    /// The new client order ID for the order after being amended. <br> If not sent, one will be randomly generated. <br> It is possible to reuse the current clientOrderId by sending it as the `newClientOrderId`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_client_order_id: Option<String>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderListCancelParams {
    /// Create a builder for [`order_list_cancel`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    ///
    #[must_use]
    pub fn builder(symbol: String) -> OrderListCancelParamsBuilder {
        OrderListCancelParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`order_list_place`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_list_place`](#method.order_list_place).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderListPlaceParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub side: OrderListPlaceSideEnum,
    /// Price for the limit order
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub price: rust_decimal::Decimal,
    ///
    /// The `quantity` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub quantity: rust_decimal::Decimal,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    ///
    /// The `list_client_order_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub list_client_order_id: Option<String>,
    /// Arbitrary unique ID among open orders for the limit order. Automatically generated if not sent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit_client_order_id: Option<String>,
    ///
    /// The `limit_iceberg_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit_iceberg_qty: Option<rust_decimal::Decimal>,
    /// Arbitrary numeric value identifying the limit order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit_strategy_id: Option<i64>,
    /// <p>Arbitrary numeric value identifying the limit order strategy.</p><p>Values smaller than `1000000` are reserved and cannot be used.</p>
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit_strategy_type: Option<i32>,
    ///
    /// The `stop_price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_price: Option<rust_decimal::Decimal>,
    /// See [Trailing Stop order FAQ](faqs/trailing-stop-faq.md)
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub trailing_delta: Option<i32>,
    /// Arbitrary unique ID among open orders for the stop order. Automatically generated if not sent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_client_order_id: Option<String>,
    ///
    /// The `stop_limit_price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_limit_price: Option<rust_decimal::Decimal>,
    ///
    /// The `stop_limit_time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_limit_time_in_force: Option<OrderListPlaceStopLimitTimeInForceEnum>,
    ///
    /// The `stop_iceberg_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_iceberg_qty: Option<rust_decimal::Decimal>,
    /// Arbitrary numeric value identifying the stop order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_strategy_id: Option<i64>,
    /// <p>Arbitrary numeric value identifying the stop order strategy.</p><p>Values smaller than `1000000` are reserved and cannot be used.</p>
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_strategy_type: Option<i32>,
    ///
    /// The `new_order_resp_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_order_resp_type: Option<OrderListPlaceNewOrderRespTypeEnum>,
    ///
    /// The `self_trade_prevention_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub self_trade_prevention_mode: Option<OrderListPlaceSelfTradePreventionModeEnum>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderListPlaceParams {
    /// Create a builder for [`order_list_place`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `side` — String
    /// * `price` — Price for the limit order
    /// * `quantity` — `rust_decimal::Decimal`
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        side: OrderListPlaceSideEnum,
        price: rust_decimal::Decimal,
        quantity: rust_decimal::Decimal,
    ) -> OrderListPlaceParamsBuilder {
        OrderListPlaceParamsBuilder::default()
            .symbol(symbol)
            .side(side)
            .price(price)
            .quantity(quantity)
    }
}
/// Request parameters for the [`order_list_place_oco`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_list_place_oco`](#method.order_list_place_oco).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderListPlaceOcoParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub side: OrderListPlaceOcoSideEnum,
    ///
    /// The `quantity` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub quantity: rust_decimal::Decimal,
    ///
    /// The `above_type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub above_type: OrderListPlaceOcoAboveTypeEnum,
    ///
    /// The `below_type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub below_type: OrderListPlaceOcoBelowTypeEnum,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    ///
    /// The `list_client_order_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub list_client_order_id: Option<String>,
    /// Arbitrary unique ID among open orders for the above order. Automatically generated if not sent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_client_order_id: Option<String>,
    /// Note that this can only be used if `aboveTimeInForce` is `GTC`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_iceberg_qty: Option<i64>,
    /// Can be used if `aboveType` is `STOP_LOSS_LIMIT` , `LIMIT_MAKER`, or `TAKE_PROFIT_LIMIT` to specify the limit price.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_price: Option<rust_decimal::Decimal>,
    /// Can be used if `aboveType` is `STOP_LOSS`, `STOP_LOSS_LIMIT`, `TAKE_PROFIT`, `TAKE_PROFIT_LIMIT`. <br>Either `aboveStopPrice` or `aboveTrailingDelta` or both, must be specified.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_stop_price: Option<rust_decimal::Decimal>,
    /// See [Trailing Stop order FAQ](faqs/trailing-stop-faq.md).
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_trailing_delta: Option<i64>,
    /// Required if `aboveType` is `STOP_LOSS_LIMIT` or `TAKE_PROFIT_LIMIT`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_time_in_force: Option<rust_decimal::Decimal>,
    /// Arbitrary numeric value identifying the above order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the above order strategy. <br>Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_strategy_type: Option<i32>,
    ///
    /// The `above_peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_peg_price_type: Option<OrderListPlaceOcoAbovePegPriceTypeEnum>,
    ///
    /// The `above_peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_peg_offset_type: Option<OrderListPlaceOcoAbovePegOffsetTypeEnum>,
    ///
    /// The `above_peg_offset_value` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub above_peg_offset_value: Option<i32>,
    ///
    /// The `below_client_order_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_client_order_id: Option<String>,
    /// Note that this can only be used if `belowTimeInForce` is `GTC`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_iceberg_qty: Option<i64>,
    /// Can be used if `belowType` is `STOP_LOSS_LIMIT` , `LIMIT_MAKER`, or `TAKE_PROFIT_LIMIT` to specify the limit price.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_price: Option<rust_decimal::Decimal>,
    /// Can be used if `belowType` is `STOP_LOSS`, `STOP_LOSS_LIMIT`, `TAKE_PROFIT` or `TAKE_PROFIT_LIMIT`. <br>Either `belowStopPrice` or `belowTrailingDelta` or both, must be specified.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_stop_price: Option<rust_decimal::Decimal>,
    /// See [Trailing Stop order FAQ](faqs/trailing-stop-faq.md).
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_trailing_delta: Option<i64>,
    ///
    /// The `below_time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_time_in_force: Option<OrderListPlaceOcoBelowTimeInForceEnum>,
    /// Arbitrary numeric value identifying the below order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the below order strategy. <br>Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_strategy_type: Option<i32>,
    ///
    /// The `below_peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_peg_price_type: Option<OrderListPlaceOcoBelowPegPriceTypeEnum>,
    ///
    /// The `below_peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_peg_offset_type: Option<OrderListPlaceOcoBelowPegOffsetTypeEnum>,
    ///
    /// The `below_peg_offset_value` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub below_peg_offset_value: Option<i32>,
    ///
    /// The `new_order_resp_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_order_resp_type: Option<OrderListPlaceOcoNewOrderRespTypeEnum>,
    ///
    /// The `self_trade_prevention_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub self_trade_prevention_mode: Option<OrderListPlaceOcoSelfTradePreventionModeEnum>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderListPlaceOcoParams {
    /// Create a builder for [`order_list_place_oco`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `side` — String
    /// * `quantity` — `rust_decimal::Decimal`
    /// * `above_type` — String
    /// * `below_type` — String
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        side: OrderListPlaceOcoSideEnum,
        quantity: rust_decimal::Decimal,
        above_type: OrderListPlaceOcoAboveTypeEnum,
        below_type: OrderListPlaceOcoBelowTypeEnum,
    ) -> OrderListPlaceOcoParamsBuilder {
        OrderListPlaceOcoParamsBuilder::default()
            .symbol(symbol)
            .side(side)
            .quantity(quantity)
            .above_type(above_type)
            .below_type(below_type)
    }
}
/// Request parameters for the [`order_list_place_oto`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_list_place_oto`](#method.order_list_place_oto).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderListPlaceOtoParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `working_type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub working_type: OrderListPlaceOtoWorkingTypeEnum,
    ///
    /// The `working_side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub working_side: OrderListPlaceOtoWorkingSideEnum,
    ///
    /// The `working_price` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub working_price: rust_decimal::Decimal,
    /// Sets the quantity for the working order.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub working_quantity: rust_decimal::Decimal,
    ///
    /// The `pending_type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub pending_type: OrderListPlaceOtoPendingTypeEnum,
    ///
    /// The `pending_side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub pending_side: OrderListPlaceOtoPendingSideEnum,
    /// Sets the quantity for the pending order.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub pending_quantity: rust_decimal::Decimal,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    ///
    /// The `list_client_order_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub list_client_order_id: Option<String>,
    ///
    /// The `new_order_resp_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_order_resp_type: Option<OrderListPlaceOtoNewOrderRespTypeEnum>,
    ///
    /// The `self_trade_prevention_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub self_trade_prevention_mode: Option<OrderListPlaceOtoSelfTradePreventionModeEnum>,
    /// Arbitrary unique ID among open orders for the working order.<br> Automatically generated if not sent.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_client_order_id: Option<String>,
    /// This can only be used if `workingTimeInForce` is `GTC`, or if `workingType` is `LIMIT_MAKER`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_iceberg_qty: Option<rust_decimal::Decimal>,
    ///
    /// The `working_time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_time_in_force: Option<OrderListPlaceOtoWorkingTimeInForceEnum>,
    /// Arbitrary numeric value identifying the working order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the working order strategy. <br> Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_strategy_type: Option<i32>,
    ///
    /// The `working_peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_peg_price_type: Option<OrderListPlaceOtoWorkingPegPriceTypeEnum>,
    ///
    /// The `working_peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_peg_offset_type: Option<OrderListPlaceOtoWorkingPegOffsetTypeEnum>,
    ///
    /// The `working_peg_offset_value` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_peg_offset_value: Option<i32>,
    /// Arbitrary unique ID among open orders for the pending order.<br> Automatically generated if not sent.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_client_order_id: Option<String>,
    ///
    /// The `pending_price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_price: Option<rust_decimal::Decimal>,
    ///
    /// The `pending_stop_price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_stop_price: Option<rust_decimal::Decimal>,
    ///
    /// The `pending_trailing_delta` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_trailing_delta: Option<rust_decimal::Decimal>,
    /// This can only be used if `pendingTimeInForce` is `GTC`, or if `pendingType` is `LIMIT_MAKER`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_iceberg_qty: Option<rust_decimal::Decimal>,
    ///
    /// The `pending_time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_time_in_force: Option<OrderListPlaceOtoPendingTimeInForceEnum>,
    /// Arbitrary numeric value identifying the pending order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the pending order strategy. <br> Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_strategy_type: Option<i32>,
    ///
    /// The `pending_peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_peg_offset_type: Option<OrderListPlaceOtoPendingPegOffsetTypeEnum>,
    ///
    /// The `pending_peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_peg_price_type: Option<OrderListPlaceOtoPendingPegPriceTypeEnum>,
    ///
    /// The `pending_peg_offset_value` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_peg_offset_value: Option<i32>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderListPlaceOtoParams {
    /// Create a builder for [`order_list_place_oto`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `working_type` — String
    /// * `working_side` — String
    /// * `working_price` — `rust_decimal::Decimal`
    /// * `working_quantity` — Sets the quantity for the working order.
    /// * `pending_type` — String
    /// * `pending_side` — String
    /// * `pending_quantity` — Sets the quantity for the pending order.
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        working_type: OrderListPlaceOtoWorkingTypeEnum,
        working_side: OrderListPlaceOtoWorkingSideEnum,
        working_price: rust_decimal::Decimal,
        working_quantity: rust_decimal::Decimal,
        pending_type: OrderListPlaceOtoPendingTypeEnum,
        pending_side: OrderListPlaceOtoPendingSideEnum,
        pending_quantity: rust_decimal::Decimal,
    ) -> OrderListPlaceOtoParamsBuilder {
        OrderListPlaceOtoParamsBuilder::default()
            .symbol(symbol)
            .working_type(working_type)
            .working_side(working_side)
            .working_price(working_price)
            .working_quantity(working_quantity)
            .pending_type(pending_type)
            .pending_side(pending_side)
            .pending_quantity(pending_quantity)
    }
}
/// Request parameters for the [`order_list_place_otoco`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_list_place_otoco`](#method.order_list_place_otoco).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderListPlaceOtocoParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `working_type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub working_type: OrderListPlaceOtocoWorkingTypeEnum,
    ///
    /// The `working_side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub working_side: OrderListPlaceOtocoWorkingSideEnum,
    ///
    /// The `working_price` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub working_price: rust_decimal::Decimal,
    /// Sets the quantity for the working order.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub working_quantity: rust_decimal::Decimal,
    ///
    /// The `pending_side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub pending_side: OrderListPlaceOtocoPendingSideEnum,
    /// Sets the quantity for the pending order.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub pending_quantity: rust_decimal::Decimal,
    ///
    /// The `pending_above_type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub pending_above_type: OrderListPlaceOtocoPendingAboveTypeEnum,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    ///
    /// The `list_client_order_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub list_client_order_id: Option<String>,
    ///
    /// The `new_order_resp_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_order_resp_type: Option<OrderListPlaceOtocoNewOrderRespTypeEnum>,
    ///
    /// The `self_trade_prevention_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub self_trade_prevention_mode: Option<OrderListPlaceOtocoSelfTradePreventionModeEnum>,
    /// Arbitrary unique ID among open orders for the working order.<br> Automatically generated if not sent.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_client_order_id: Option<String>,
    /// This can only be used if `workingTimeInForce` is `GTC`, or if `workingType` is `LIMIT_MAKER`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_iceberg_qty: Option<rust_decimal::Decimal>,
    ///
    /// The `working_time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_time_in_force: Option<OrderListPlaceOtocoWorkingTimeInForceEnum>,
    /// Arbitrary numeric value identifying the working order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the working order strategy. <br> Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_strategy_type: Option<i32>,
    ///
    /// The `working_peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_peg_price_type: Option<OrderListPlaceOtocoWorkingPegPriceTypeEnum>,
    ///
    /// The `working_peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_peg_offset_type: Option<OrderListPlaceOtocoWorkingPegOffsetTypeEnum>,
    ///
    /// The `working_peg_offset_value` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub working_peg_offset_value: Option<i32>,
    /// Arbitrary unique ID among open orders for the pending above order.<br> Automatically generated if not sent.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_client_order_id: Option<String>,
    /// Can be used if `pendingAboveType` is `STOP_LOSS_LIMIT` , `LIMIT_MAKER`, or `TAKE_PROFIT_LIMIT` to specify the limit price.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_price: Option<rust_decimal::Decimal>,
    /// Can be used if `pendingAboveType` is `STOP_LOSS`, `STOP_LOSS_LIMIT`, `TAKE_PROFIT`, `TAKE_PROFIT_LIMIT`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_stop_price: Option<rust_decimal::Decimal>,
    /// See [Trailing Stop FAQ](faqs/trailing-stop-faq.md)
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_trailing_delta: Option<rust_decimal::Decimal>,
    /// This can only be used if `pendingAboveTimeInForce` is `GTC` or if `pendingAboveType` is `LIMIT_MAKER`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_iceberg_qty: Option<rust_decimal::Decimal>,
    ///
    /// The `pending_above_time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_time_in_force: Option<OrderListPlaceOtocoPendingAboveTimeInForceEnum>,
    /// Arbitrary numeric value identifying the pending above order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the pending above order strategy. <br> Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_strategy_type: Option<i32>,
    ///
    /// The `pending_above_peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_peg_price_type: Option<OrderListPlaceOtocoPendingAbovePegPriceTypeEnum>,
    ///
    /// The `pending_above_peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_peg_offset_type: Option<OrderListPlaceOtocoPendingAbovePegOffsetTypeEnum>,
    ///
    /// The `pending_above_peg_offset_value` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_above_peg_offset_value: Option<i32>,
    ///
    /// The `pending_below_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_type: Option<OrderListPlaceOtocoPendingBelowTypeEnum>,
    /// Arbitrary unique ID among open orders for the pending below order.<br> Automatically generated if not sent.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_client_order_id: Option<String>,
    /// Can be used if `pendingBelowType` is `STOP_LOSS_LIMIT` or `TAKE_PROFIT_LIMIT` to specify the limit price.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_price: Option<rust_decimal::Decimal>,
    /// Can be used if `pendingBelowType` is `STOP_LOSS`, `STOP_LOSS_LIMIT, TAKE_PROFIT or TAKE_PROFIT_LIMIT`. <br>Either `pendingBelowStopPrice` or `pendingBelowTrailingDelta` or both, must be specified.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_stop_price: Option<rust_decimal::Decimal>,
    ///
    /// The `pending_below_trailing_delta` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_trailing_delta: Option<rust_decimal::Decimal>,
    /// This can only be used if `pendingBelowTimeInForce` is `GTC`, or if `pendingBelowType` is `LIMIT_MAKER`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_iceberg_qty: Option<rust_decimal::Decimal>,
    ///
    /// The `pending_below_time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_time_in_force: Option<OrderListPlaceOtocoPendingBelowTimeInForceEnum>,
    /// Arbitrary numeric value identifying the pending below order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the pending below order strategy. <br> Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_strategy_type: Option<i32>,
    ///
    /// The `pending_below_peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_peg_price_type: Option<OrderListPlaceOtocoPendingBelowPegPriceTypeEnum>,
    ///
    /// The `pending_below_peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_peg_offset_type: Option<OrderListPlaceOtocoPendingBelowPegOffsetTypeEnum>,
    ///
    /// The `pending_below_peg_offset_value` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub pending_below_peg_offset_value: Option<i32>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderListPlaceOtocoParams {
    /// Create a builder for [`order_list_place_otoco`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `working_type` — String
    /// * `working_side` — String
    /// * `working_price` — `rust_decimal::Decimal`
    /// * `working_quantity` — Sets the quantity for the working order.
    /// * `pending_side` — String
    /// * `pending_quantity` — Sets the quantity for the pending order.
    /// * `pending_above_type` — String
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        working_type: OrderListPlaceOtocoWorkingTypeEnum,
        working_side: OrderListPlaceOtocoWorkingSideEnum,
        working_price: rust_decimal::Decimal,
        working_quantity: rust_decimal::Decimal,
        pending_side: OrderListPlaceOtocoPendingSideEnum,
        pending_quantity: rust_decimal::Decimal,
        pending_above_type: OrderListPlaceOtocoPendingAboveTypeEnum,
    ) -> OrderListPlaceOtocoParamsBuilder {
        OrderListPlaceOtocoParamsBuilder::default()
            .symbol(symbol)
            .working_type(working_type)
            .working_side(working_side)
            .working_price(working_price)
            .working_quantity(working_quantity)
            .pending_side(pending_side)
            .pending_quantity(pending_quantity)
            .pending_above_type(pending_above_type)
    }
}
/// Request parameters for the [`order_place`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_place`](#method.order_place).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderPlaceParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub side: OrderPlaceSideEnum,
    ///
    /// The `r#type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub r#type: OrderPlaceTypeEnum,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    ///
    /// The `time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub time_in_force: Option<OrderPlaceTimeInForceEnum>,
    ///
    /// The `price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub price: Option<rust_decimal::Decimal>,
    ///
    /// The `quantity` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub quantity: Option<rust_decimal::Decimal>,
    ///
    /// The `quote_order_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub quote_order_qty: Option<rust_decimal::Decimal>,
    /// The new client order ID for the order after being amended. <br> If not sent, one will be randomly generated. <br> It is possible to reuse the current clientOrderId by sending it as the `newClientOrderId`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_client_order_id: Option<String>,
    ///
    /// The `new_order_resp_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_order_resp_type: Option<OrderPlaceNewOrderRespTypeEnum>,
    ///
    /// The `stop_price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_price: Option<rust_decimal::Decimal>,
    /// See [Trailing Stop order FAQ](faqs/trailing-stop-faq.md)
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub trailing_delta: Option<i32>,
    ///
    /// The `iceberg_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub iceberg_qty: Option<rust_decimal::Decimal>,
    /// Arbitrary numeric value identifying the order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the order strategy.
    /// Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_type: Option<i32>,
    ///
    /// The `self_trade_prevention_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub self_trade_prevention_mode: Option<OrderPlaceSelfTradePreventionModeEnum>,
    ///
    /// The `peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub peg_price_type: Option<OrderPlacePegPriceTypeEnum>,
    /// Price level to peg the price to (max: 100)
    /// See Pegged Orders
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub peg_offset_value: Option<i32>,
    ///
    /// The `peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub peg_offset_type: Option<OrderPlacePegOffsetTypeEnum>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderPlaceParams {
    /// Create a builder for [`order_place`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `side` — String
    /// * `r#type` — String
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        side: OrderPlaceSideEnum,
        r#type: OrderPlaceTypeEnum,
    ) -> OrderPlaceParamsBuilder {
        OrderPlaceParamsBuilder::default()
            .symbol(symbol)
            .side(side)
            .r#type(r#type)
    }
}
/// Request parameters for the [`order_test`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_test`](#method.order_test).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderTestParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub side: OrderTestSideEnum,
    ///
    /// The `r#type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub r#type: OrderTestTypeEnum,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// Default: `false` <br> See [Commissions FAQ](faqs/commission_faq.md#test-order-diferences) to learn more.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub compute_commission_rates: Option<bool>,
    ///
    /// The `time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub time_in_force: Option<OrderTestTimeInForceEnum>,
    ///
    /// The `price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub price: Option<rust_decimal::Decimal>,
    ///
    /// The `quantity` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub quantity: Option<rust_decimal::Decimal>,
    ///
    /// The `quote_order_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub quote_order_qty: Option<rust_decimal::Decimal>,
    /// The new client order ID for the order after being amended. <br> If not sent, one will be randomly generated. <br> It is possible to reuse the current clientOrderId by sending it as the `newClientOrderId`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_client_order_id: Option<String>,
    ///
    /// The `new_order_resp_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_order_resp_type: Option<OrderTestNewOrderRespTypeEnum>,
    ///
    /// The `stop_price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub stop_price: Option<rust_decimal::Decimal>,
    /// See [Trailing Stop order FAQ](faqs/trailing-stop-faq.md)
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub trailing_delta: Option<i32>,
    ///
    /// The `iceberg_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub iceberg_qty: Option<rust_decimal::Decimal>,
    /// Arbitrary numeric value identifying the order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the order strategy.
    /// Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_type: Option<i32>,
    ///
    /// The `self_trade_prevention_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub self_trade_prevention_mode: Option<OrderTestSelfTradePreventionModeEnum>,
    ///
    /// The `peg_price_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub peg_price_type: Option<OrderTestPegPriceTypeEnum>,
    /// Price level to peg the price to (max: 100)
    /// See Pegged Orders
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub peg_offset_value: Option<i32>,
    ///
    /// The `peg_offset_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub peg_offset_type: Option<OrderTestPegOffsetTypeEnum>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderTestParams {
    /// Create a builder for [`order_test`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `side` — String
    /// * `r#type` — String
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        side: OrderTestSideEnum,
        r#type: OrderTestTypeEnum,
    ) -> OrderTestParamsBuilder {
        OrderTestParamsBuilder::default()
            .symbol(symbol)
            .side(side)
            .r#type(r#type)
    }
}
/// Request parameters for the [`sor_order_place`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`sor_order_place`](#method.sor_order_place).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct SorOrderPlaceParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub side: SorOrderPlaceSideEnum,
    ///
    /// The `r#type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub r#type: SorOrderPlaceTypeEnum,
    ///
    /// The `quantity` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub quantity: rust_decimal::Decimal,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    ///
    /// The `time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub time_in_force: Option<SorOrderPlaceTimeInForceEnum>,
    ///
    /// The `price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub price: Option<rust_decimal::Decimal>,
    /// The new client order ID for the order after being amended. <br> If not sent, one will be randomly generated. <br> It is possible to reuse the current clientOrderId by sending it as the `newClientOrderId`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_client_order_id: Option<String>,
    ///
    /// The `new_order_resp_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_order_resp_type: Option<SorOrderPlaceNewOrderRespTypeEnum>,
    ///
    /// The `iceberg_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub iceberg_qty: Option<rust_decimal::Decimal>,
    /// Arbitrary numeric value identifying the order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the order strategy.
    /// Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_type: Option<i32>,
    ///
    /// The `self_trade_prevention_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub self_trade_prevention_mode: Option<SorOrderPlaceSelfTradePreventionModeEnum>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl SorOrderPlaceParams {
    /// Create a builder for [`sor_order_place`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `side` — String
    /// * `r#type` — String
    /// * `quantity` — `rust_decimal::Decimal`
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        side: SorOrderPlaceSideEnum,
        r#type: SorOrderPlaceTypeEnum,
        quantity: rust_decimal::Decimal,
    ) -> SorOrderPlaceParamsBuilder {
        SorOrderPlaceParamsBuilder::default()
            .symbol(symbol)
            .side(side)
            .r#type(r#type)
            .quantity(quantity)
    }
}
/// Request parameters for the [`sor_order_test`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`sor_order_test`](#method.sor_order_test).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct SorOrderTestParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `side` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub side: SorOrderTestSideEnum,
    ///
    /// The `r#type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub r#type: SorOrderTestTypeEnum,
    ///
    /// The `quantity` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub quantity: rust_decimal::Decimal,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// Default: `false` <br> See [Commissions FAQ](faqs/commission_faq.md#test-order-diferences) to learn more.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub compute_commission_rates: Option<bool>,
    ///
    /// The `time_in_force` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub time_in_force: Option<SorOrderTestTimeInForceEnum>,
    ///
    /// The `price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub price: Option<rust_decimal::Decimal>,
    /// The new client order ID for the order after being amended. <br> If not sent, one will be randomly generated. <br> It is possible to reuse the current clientOrderId by sending it as the `newClientOrderId`.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_client_order_id: Option<String>,
    ///
    /// The `new_order_resp_type` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub new_order_resp_type: Option<SorOrderTestNewOrderRespTypeEnum>,
    ///
    /// The `iceberg_qty` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub iceberg_qty: Option<rust_decimal::Decimal>,
    /// Arbitrary numeric value identifying the order within an order strategy.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_id: Option<i64>,
    /// Arbitrary numeric value identifying the order strategy.
    /// Values smaller than 1000000 are reserved and cannot be used.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub strategy_type: Option<i32>,
    ///
    /// The `self_trade_prevention_mode` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub self_trade_prevention_mode: Option<SorOrderTestSelfTradePreventionModeEnum>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl SorOrderTestParams {
    /// Create a builder for [`sor_order_test`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `side` — String
    /// * `r#type` — String
    /// * `quantity` — `rust_decimal::Decimal`
    ///
    #[must_use]
    pub fn builder(
        symbol: String,
        side: SorOrderTestSideEnum,
        r#type: SorOrderTestTypeEnum,
        quantity: rust_decimal::Decimal,
    ) -> SorOrderTestParamsBuilder {
        SorOrderTestParamsBuilder::default()
            .symbol(symbol)
            .side(side)
            .r#type(r#type)
            .quantity(quantity)
    }
}

#[async_trait]
impl TradeApi for TradeApiClient {
    async fn open_orders_cancel_all(
        &self,
        params: OpenOrdersCancelAllParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrdersCancelAllResponseResultInner>>>
    {
        let OpenOrdersCancelAllParams {
            symbol,
            id,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::OpenOrdersCancelAllResponseResultInner>>(
                "/openOrders.cancelAll".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_amend_keep_priority(
        &self,
        params: OrderAmendKeepPriorityParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderAmendKeepPriorityResponseResult>>>
    {
        let OrderAmendKeepPriorityParams {
            symbol,
            new_qty,
            id,
            order_id,
            orig_client_order_id,
            new_client_order_id,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("newQty".to_string(), serde_json::json!(new_qty));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_id {
            payload.insert("orderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = orig_client_order_id {
            payload.insert("origClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_client_order_id {
            payload.insert("newClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderAmendKeepPriorityResponseResult>>(
                "/order.amend.keepPriority".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_cancel(
        &self,
        params: OrderCancelParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderCancelResponseResult>>> {
        let OrderCancelParams {
            symbol,
            id,
            order_id,
            orig_client_order_id,
            new_client_order_id,
            cancel_restrictions,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_id {
            payload.insert("orderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = orig_client_order_id {
            payload.insert("origClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_client_order_id {
            payload.insert("newClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = cancel_restrictions {
            payload.insert("cancelRestrictions".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderCancelResponseResult>>(
                "/order.cancel".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_cancel_replace(
        &self,
        params: OrderCancelReplaceParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderCancelReplaceResponseResult>>> {
        let OrderCancelReplaceParams {
            symbol,
            cancel_replace_mode,
            side,
            r#type,
            id,
            cancel_order_id,
            cancel_orig_client_order_id,
            cancel_new_client_order_id,
            time_in_force,
            price,
            quantity,
            quote_order_qty,
            new_client_order_id,
            new_order_resp_type,
            stop_price,
            trailing_delta,
            iceberg_qty,
            strategy_id,
            strategy_type,
            self_trade_prevention_mode,
            cancel_restrictions,
            order_rate_limit_exceeded_mode,
            peg_price_type,
            peg_offset_value,
            peg_offset_type,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert(
            "cancelReplaceMode".to_string(),
            serde_json::json!(cancel_replace_mode),
        );
        payload.insert("side".to_string(), serde_json::json!(side));
        payload.insert("type".to_string(), serde_json::json!(r#type));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = cancel_order_id {
            payload.insert("cancelOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = cancel_orig_client_order_id {
            payload.insert(
                "cancelOrigClientOrderId".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = cancel_new_client_order_id {
            payload.insert(
                "cancelNewClientOrderId".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = time_in_force {
            payload.insert("timeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = price {
            payload.insert("price".to_string(), serde_json::json!(value));
        }
        if let Some(value) = quantity {
            payload.insert("quantity".to_string(), serde_json::json!(value));
        }
        if let Some(value) = quote_order_qty {
            payload.insert("quoteOrderQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_client_order_id {
            payload.insert("newClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_order_resp_type {
            payload.insert("newOrderRespType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_price {
            payload.insert("stopPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = trailing_delta {
            payload.insert("trailingDelta".to_string(), serde_json::json!(value));
        }
        if let Some(value) = iceberg_qty {
            payload.insert("icebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_id {
            payload.insert("strategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_type {
            payload.insert("strategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = self_trade_prevention_mode {
            payload.insert(
                "selfTradePreventionMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = cancel_restrictions {
            payload.insert("cancelRestrictions".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_rate_limit_exceeded_mode {
            payload.insert(
                "orderRateLimitExceededMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = peg_price_type {
            payload.insert("pegPriceType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = peg_offset_value {
            payload.insert("pegOffsetValue".to_string(), serde_json::json!(value));
        }
        if let Some(value) = peg_offset_type {
            payload.insert("pegOffsetType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderCancelReplaceResponseResult>>(
                "/order.cancelReplace".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_list_cancel(
        &self,
        params: OrderListCancelParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListCancelResponseResult>>> {
        let OrderListCancelParams {
            symbol,
            id,
            order_list_id,
            list_client_order_id,
            new_client_order_id,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_list_id {
            payload.insert("orderListId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = list_client_order_id {
            payload.insert("listClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_client_order_id {
            payload.insert("newClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderListCancelResponseResult>>(
                "/orderList.cancel".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_list_place(
        &self,
        params: OrderListPlaceParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListPlaceResponseResult>>> {
        let OrderListPlaceParams {
            symbol,
            side,
            price,
            quantity,
            id,
            list_client_order_id,
            limit_client_order_id,
            limit_iceberg_qty,
            limit_strategy_id,
            limit_strategy_type,
            stop_price,
            trailing_delta,
            stop_client_order_id,
            stop_limit_price,
            stop_limit_time_in_force,
            stop_iceberg_qty,
            stop_strategy_id,
            stop_strategy_type,
            new_order_resp_type,
            self_trade_prevention_mode,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("side".to_string(), serde_json::json!(side));
        payload.insert("price".to_string(), serde_json::json!(price));
        payload.insert("quantity".to_string(), serde_json::json!(quantity));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = list_client_order_id {
            payload.insert("listClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit_client_order_id {
            payload.insert("limitClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit_iceberg_qty {
            payload.insert("limitIcebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit_strategy_id {
            payload.insert("limitStrategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit_strategy_type {
            payload.insert("limitStrategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_price {
            payload.insert("stopPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = trailing_delta {
            payload.insert("trailingDelta".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_client_order_id {
            payload.insert("stopClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_limit_price {
            payload.insert("stopLimitPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_limit_time_in_force {
            payload.insert("stopLimitTimeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_iceberg_qty {
            payload.insert("stopIcebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_strategy_id {
            payload.insert("stopStrategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_strategy_type {
            payload.insert("stopStrategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_order_resp_type {
            payload.insert("newOrderRespType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = self_trade_prevention_mode {
            payload.insert(
                "selfTradePreventionMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderListPlaceResponseResult>>(
                "/orderList.place".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_list_place_oco(
        &self,
        params: OrderListPlaceOcoParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListPlaceOcoResponseResult>>> {
        let OrderListPlaceOcoParams {
            symbol,
            side,
            quantity,
            above_type,
            below_type,
            id,
            list_client_order_id,
            above_client_order_id,
            above_iceberg_qty,
            above_price,
            above_stop_price,
            above_trailing_delta,
            above_time_in_force,
            above_strategy_id,
            above_strategy_type,
            above_peg_price_type,
            above_peg_offset_type,
            above_peg_offset_value,
            below_client_order_id,
            below_iceberg_qty,
            below_price,
            below_stop_price,
            below_trailing_delta,
            below_time_in_force,
            below_strategy_id,
            below_strategy_type,
            below_peg_price_type,
            below_peg_offset_type,
            below_peg_offset_value,
            new_order_resp_type,
            self_trade_prevention_mode,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("side".to_string(), serde_json::json!(side));
        payload.insert("quantity".to_string(), serde_json::json!(quantity));
        payload.insert("aboveType".to_string(), serde_json::json!(above_type));
        payload.insert("belowType".to_string(), serde_json::json!(below_type));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = list_client_order_id {
            payload.insert("listClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_client_order_id {
            payload.insert("aboveClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_iceberg_qty {
            payload.insert("aboveIcebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_price {
            payload.insert("abovePrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_stop_price {
            payload.insert("aboveStopPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_trailing_delta {
            payload.insert("aboveTrailingDelta".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_time_in_force {
            payload.insert("aboveTimeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_strategy_id {
            payload.insert("aboveStrategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_strategy_type {
            payload.insert("aboveStrategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_peg_price_type {
            payload.insert("abovePegPriceType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_peg_offset_type {
            payload.insert("abovePegOffsetType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = above_peg_offset_value {
            payload.insert("abovePegOffsetValue".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_client_order_id {
            payload.insert("belowClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_iceberg_qty {
            payload.insert("belowIcebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_price {
            payload.insert("belowPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_stop_price {
            payload.insert("belowStopPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_trailing_delta {
            payload.insert("belowTrailingDelta".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_time_in_force {
            payload.insert("belowTimeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_strategy_id {
            payload.insert("belowStrategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_strategy_type {
            payload.insert("belowStrategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_peg_price_type {
            payload.insert("belowPegPriceType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_peg_offset_type {
            payload.insert("belowPegOffsetType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = below_peg_offset_value {
            payload.insert("belowPegOffsetValue".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_order_resp_type {
            payload.insert("newOrderRespType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = self_trade_prevention_mode {
            payload.insert(
                "selfTradePreventionMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderListPlaceOcoResponseResult>>(
                "/orderList.place.oco".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_list_place_oto(
        &self,
        params: OrderListPlaceOtoParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListPlaceOtoResponseResult>>> {
        let OrderListPlaceOtoParams {
            symbol,
            working_type,
            working_side,
            working_price,
            working_quantity,
            pending_type,
            pending_side,
            pending_quantity,
            id,
            list_client_order_id,
            new_order_resp_type,
            self_trade_prevention_mode,
            working_client_order_id,
            working_iceberg_qty,
            working_time_in_force,
            working_strategy_id,
            working_strategy_type,
            working_peg_price_type,
            working_peg_offset_type,
            working_peg_offset_value,
            pending_client_order_id,
            pending_price,
            pending_stop_price,
            pending_trailing_delta,
            pending_iceberg_qty,
            pending_time_in_force,
            pending_strategy_id,
            pending_strategy_type,
            pending_peg_offset_type,
            pending_peg_price_type,
            pending_peg_offset_value,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("workingType".to_string(), serde_json::json!(working_type));
        payload.insert("workingSide".to_string(), serde_json::json!(working_side));
        payload.insert("workingPrice".to_string(), serde_json::json!(working_price));
        payload.insert(
            "workingQuantity".to_string(),
            serde_json::json!(working_quantity),
        );
        payload.insert("pendingType".to_string(), serde_json::json!(pending_type));
        payload.insert("pendingSide".to_string(), serde_json::json!(pending_side));
        payload.insert(
            "pendingQuantity".to_string(),
            serde_json::json!(pending_quantity),
        );
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = list_client_order_id {
            payload.insert("listClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_order_resp_type {
            payload.insert("newOrderRespType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = self_trade_prevention_mode {
            payload.insert(
                "selfTradePreventionMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = working_client_order_id {
            payload.insert("workingClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_iceberg_qty {
            payload.insert("workingIcebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_time_in_force {
            payload.insert("workingTimeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_strategy_id {
            payload.insert("workingStrategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_strategy_type {
            payload.insert("workingStrategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_peg_price_type {
            payload.insert("workingPegPriceType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_peg_offset_type {
            payload.insert("workingPegOffsetType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_peg_offset_value {
            payload.insert(
                "workingPegOffsetValue".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_client_order_id {
            payload.insert("pendingClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_price {
            payload.insert("pendingPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_stop_price {
            payload.insert("pendingStopPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_trailing_delta {
            payload.insert("pendingTrailingDelta".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_iceberg_qty {
            payload.insert("pendingIcebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_time_in_force {
            payload.insert("pendingTimeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_strategy_id {
            payload.insert("pendingStrategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_strategy_type {
            payload.insert("pendingStrategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_peg_offset_type {
            payload.insert("pendingPegOffsetType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_peg_price_type {
            payload.insert("pendingPegPriceType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_peg_offset_value {
            payload.insert(
                "pendingPegOffsetValue".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderListPlaceOtoResponseResult>>(
                "/orderList.place.oto".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_list_place_otoco(
        &self,
        params: OrderListPlaceOtocoParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderListPlaceOtocoResponseResult>>> {
        let OrderListPlaceOtocoParams {
            symbol,
            working_type,
            working_side,
            working_price,
            working_quantity,
            pending_side,
            pending_quantity,
            pending_above_type,
            id,
            list_client_order_id,
            new_order_resp_type,
            self_trade_prevention_mode,
            working_client_order_id,
            working_iceberg_qty,
            working_time_in_force,
            working_strategy_id,
            working_strategy_type,
            working_peg_price_type,
            working_peg_offset_type,
            working_peg_offset_value,
            pending_above_client_order_id,
            pending_above_price,
            pending_above_stop_price,
            pending_above_trailing_delta,
            pending_above_iceberg_qty,
            pending_above_time_in_force,
            pending_above_strategy_id,
            pending_above_strategy_type,
            pending_above_peg_price_type,
            pending_above_peg_offset_type,
            pending_above_peg_offset_value,
            pending_below_type,
            pending_below_client_order_id,
            pending_below_price,
            pending_below_stop_price,
            pending_below_trailing_delta,
            pending_below_iceberg_qty,
            pending_below_time_in_force,
            pending_below_strategy_id,
            pending_below_strategy_type,
            pending_below_peg_price_type,
            pending_below_peg_offset_type,
            pending_below_peg_offset_value,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("workingType".to_string(), serde_json::json!(working_type));
        payload.insert("workingSide".to_string(), serde_json::json!(working_side));
        payload.insert("workingPrice".to_string(), serde_json::json!(working_price));
        payload.insert(
            "workingQuantity".to_string(),
            serde_json::json!(working_quantity),
        );
        payload.insert("pendingSide".to_string(), serde_json::json!(pending_side));
        payload.insert(
            "pendingQuantity".to_string(),
            serde_json::json!(pending_quantity),
        );
        payload.insert(
            "pendingAboveType".to_string(),
            serde_json::json!(pending_above_type),
        );
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = list_client_order_id {
            payload.insert("listClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_order_resp_type {
            payload.insert("newOrderRespType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = self_trade_prevention_mode {
            payload.insert(
                "selfTradePreventionMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = working_client_order_id {
            payload.insert("workingClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_iceberg_qty {
            payload.insert("workingIcebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_time_in_force {
            payload.insert("workingTimeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_strategy_id {
            payload.insert("workingStrategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_strategy_type {
            payload.insert("workingStrategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_peg_price_type {
            payload.insert("workingPegPriceType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_peg_offset_type {
            payload.insert("workingPegOffsetType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = working_peg_offset_value {
            payload.insert(
                "workingPegOffsetValue".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_client_order_id {
            payload.insert(
                "pendingAboveClientOrderId".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_price {
            payload.insert("pendingAbovePrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_above_stop_price {
            payload.insert(
                "pendingAboveStopPrice".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_trailing_delta {
            payload.insert(
                "pendingAboveTrailingDelta".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_iceberg_qty {
            payload.insert(
                "pendingAboveIcebergQty".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_time_in_force {
            payload.insert(
                "pendingAboveTimeInForce".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_strategy_id {
            payload.insert(
                "pendingAboveStrategyId".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_strategy_type {
            payload.insert(
                "pendingAboveStrategyType".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_peg_price_type {
            payload.insert(
                "pendingAbovePegPriceType".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_peg_offset_type {
            payload.insert(
                "pendingAbovePegOffsetType".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_above_peg_offset_value {
            payload.insert(
                "pendingAbovePegOffsetValue".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_type {
            payload.insert("pendingBelowType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_below_client_order_id {
            payload.insert(
                "pendingBelowClientOrderId".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_price {
            payload.insert("pendingBelowPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = pending_below_stop_price {
            payload.insert(
                "pendingBelowStopPrice".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_trailing_delta {
            payload.insert(
                "pendingBelowTrailingDelta".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_iceberg_qty {
            payload.insert(
                "pendingBelowIcebergQty".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_time_in_force {
            payload.insert(
                "pendingBelowTimeInForce".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_strategy_id {
            payload.insert(
                "pendingBelowStrategyId".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_strategy_type {
            payload.insert(
                "pendingBelowStrategyType".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_peg_price_type {
            payload.insert(
                "pendingBelowPegPriceType".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_peg_offset_type {
            payload.insert(
                "pendingBelowPegOffsetType".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = pending_below_peg_offset_value {
            payload.insert(
                "pendingBelowPegOffsetValue".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderListPlaceOtocoResponseResult>>(
                "/orderList.place.otoco".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_place(
        &self,
        params: OrderPlaceParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderPlaceResponseResult>>> {
        let OrderPlaceParams {
            symbol,
            side,
            r#type,
            id,
            time_in_force,
            price,
            quantity,
            quote_order_qty,
            new_client_order_id,
            new_order_resp_type,
            stop_price,
            trailing_delta,
            iceberg_qty,
            strategy_id,
            strategy_type,
            self_trade_prevention_mode,
            peg_price_type,
            peg_offset_value,
            peg_offset_type,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("side".to_string(), serde_json::json!(side));
        payload.insert("type".to_string(), serde_json::json!(r#type));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = time_in_force {
            payload.insert("timeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = price {
            payload.insert("price".to_string(), serde_json::json!(value));
        }
        if let Some(value) = quantity {
            payload.insert("quantity".to_string(), serde_json::json!(value));
        }
        if let Some(value) = quote_order_qty {
            payload.insert("quoteOrderQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_client_order_id {
            payload.insert("newClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_order_resp_type {
            payload.insert("newOrderRespType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_price {
            payload.insert("stopPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = trailing_delta {
            payload.insert("trailingDelta".to_string(), serde_json::json!(value));
        }
        if let Some(value) = iceberg_qty {
            payload.insert("icebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_id {
            payload.insert("strategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_type {
            payload.insert("strategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = self_trade_prevention_mode {
            payload.insert(
                "selfTradePreventionMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = peg_price_type {
            payload.insert("pegPriceType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = peg_offset_value {
            payload.insert("pegOffsetValue".to_string(), serde_json::json!(value));
        }
        if let Some(value) = peg_offset_type {
            payload.insert("pegOffsetType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderPlaceResponseResult>>(
                "/order.place".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_test(
        &self,
        params: OrderTestParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderTestResponseResult>>> {
        let OrderTestParams {
            symbol,
            side,
            r#type,
            id,
            compute_commission_rates,
            time_in_force,
            price,
            quantity,
            quote_order_qty,
            new_client_order_id,
            new_order_resp_type,
            stop_price,
            trailing_delta,
            iceberg_qty,
            strategy_id,
            strategy_type,
            self_trade_prevention_mode,
            peg_price_type,
            peg_offset_value,
            peg_offset_type,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("side".to_string(), serde_json::json!(side));
        payload.insert("type".to_string(), serde_json::json!(r#type));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = compute_commission_rates {
            payload.insert(
                "computeCommissionRates".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = time_in_force {
            payload.insert("timeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = price {
            payload.insert("price".to_string(), serde_json::json!(value));
        }
        if let Some(value) = quantity {
            payload.insert("quantity".to_string(), serde_json::json!(value));
        }
        if let Some(value) = quote_order_qty {
            payload.insert("quoteOrderQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_client_order_id {
            payload.insert("newClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_order_resp_type {
            payload.insert("newOrderRespType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = stop_price {
            payload.insert("stopPrice".to_string(), serde_json::json!(value));
        }
        if let Some(value) = trailing_delta {
            payload.insert("trailingDelta".to_string(), serde_json::json!(value));
        }
        if let Some(value) = iceberg_qty {
            payload.insert("icebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_id {
            payload.insert("strategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_type {
            payload.insert("strategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = self_trade_prevention_mode {
            payload.insert(
                "selfTradePreventionMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = peg_price_type {
            payload.insert("pegPriceType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = peg_offset_value {
            payload.insert("pegOffsetValue".to_string(), serde_json::json!(value));
        }
        if let Some(value) = peg_offset_type {
            payload.insert("pegOffsetType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderTestResponseResult>>(
                "/order.test".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn sor_order_place(
        &self,
        params: SorOrderPlaceParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::SorOrderPlaceResponseResultInner>>> {
        let SorOrderPlaceParams {
            symbol,
            side,
            r#type,
            quantity,
            id,
            time_in_force,
            price,
            new_client_order_id,
            new_order_resp_type,
            iceberg_qty,
            strategy_id,
            strategy_type,
            self_trade_prevention_mode,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("side".to_string(), serde_json::json!(side));
        payload.insert("type".to_string(), serde_json::json!(r#type));
        payload.insert("quantity".to_string(), serde_json::json!(quantity));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = time_in_force {
            payload.insert("timeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = price {
            payload.insert("price".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_client_order_id {
            payload.insert("newClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_order_resp_type {
            payload.insert("newOrderRespType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = iceberg_qty {
            payload.insert("icebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_id {
            payload.insert("strategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_type {
            payload.insert("strategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = self_trade_prevention_mode {
            payload.insert(
                "selfTradePreventionMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::SorOrderPlaceResponseResultInner>>(
                "/sor.order.place".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn sor_order_test(
        &self,
        params: SorOrderTestParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::SorOrderTestResponseResult>>> {
        let SorOrderTestParams {
            symbol,
            side,
            r#type,
            quantity,
            id,
            compute_commission_rates,
            time_in_force,
            price,
            new_client_order_id,
            new_order_resp_type,
            iceberg_qty,
            strategy_id,
            strategy_type,
            self_trade_prevention_mode,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("side".to_string(), serde_json::json!(side));
        payload.insert("type".to_string(), serde_json::json!(r#type));
        payload.insert("quantity".to_string(), serde_json::json!(quantity));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = compute_commission_rates {
            payload.insert(
                "computeCommissionRates".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = time_in_force {
            payload.insert("timeInForce".to_string(), serde_json::json!(value));
        }
        if let Some(value) = price {
            payload.insert("price".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_client_order_id {
            payload.insert("newClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = new_order_resp_type {
            payload.insert("newOrderRespType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = iceberg_qty {
            payload.insert("icebergQty".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_id {
            payload.insert("strategyId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = strategy_type {
            payload.insert("strategyType".to_string(), serde_json::json!(value));
        }
        if let Some(value) = self_trade_prevention_mode {
            payload.insert(
                "selfTradePreventionMode".to_string(),
                serde_json::json!(value),
            );
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::SorOrderTestResponseResult>>(
                "/sor.order.test".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }
}

#[cfg(all(test, feature = "spot"))]
mod tests {
    use super::*;
    use crate::TOKIO_SHARED_RT;
    use crate::common::websocket::{WebsocketApi, WebsocketConnection, WebsocketHandler};
    use crate::config::ConfigurationWebsocketApi;
    use crate::errors::WebsocketError;
    use crate::models::WebsocketApiRateLimit;
    use serde_json::{Value, json};
    use tokio::spawn;
    use tokio::sync::mpsc::{UnboundedReceiver, unbounded_channel};
    use tokio::time::{Duration, timeout};
    use tokio_tungstenite::tungstenite::Message;

    async fn setup() -> (
        Arc<WebsocketApi>,
        Arc<WebsocketConnection>,
        UnboundedReceiver<Message>,
    ) {
        let conn = WebsocketConnection::new("test-conn");
        let (tx, rx) = unbounded_channel::<Message>();
        {
            let mut conn_state = conn.state.lock().await;
            conn_state.ws_write_tx = Some(tx);
        }

        let config = ConfigurationWebsocketApi::builder()
            .api_key("key")
            .api_secret("secret")
            .build()
            .expect("Failed to build configuration");
        let ws_api = WebsocketApi::new(config, vec![conn.clone()]);
        conn.set_handler(ws_api.clone() as Arc<dyn WebsocketHandler>)
            .await;
        ws_api.clone().connect().await.unwrap();

        (ws_api, conn, rx)
    }

    #[test]
    fn open_orders_cancel_all_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OpenOrdersCancelAllParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.open_orders_cancel_all(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/openOrders.cancelAll".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"778f938f-9041-4b88-9914-efbf64eeacc8","status":200,"result":[{"orderListId":19431,"contingencyType":"OCO","listStatusType":"ALL_DONE","listOrderStatus":"ALL_DONE","listClientOrderId":"iuVNVJYYrByz6C4yGOPPK0","transactionTime":1660803702431,"symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":12569099454,"clientOrderId":"Tnu2IP0J5Y4mxw3IATBfmW"},{"symbol":"BTCUSDT","orderId":12569099453,"clientOrderId":"bX5wROblo6YeDwa9iTLeyY"}],"orderReports":[{"symbol":"BTCUSDT","origClientOrderId":"Tnu2IP0J5Y4mxw3IATBfmW","orderId":12569099454,"orderListId":19431,"clientOrderId":"OFFXQtxVFZ6Nbcg4PgE2DA","transactTime":1684804350068,"price":"23400.00000000","origQty":"0.00850000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"LIMIT_MAKER","side":"BUY","selfTradePreventionMode":"NONE"},{"symbol":"BTCUSDT","origClientOrderId":"bX5wROblo6YeDwa9iTLeyY","orderId":12569099453,"orderListId":19431,"clientOrderId":"OFFXQtxVFZ6Nbcg4PgE2DA","transactTime":1684804350068,"price":"23450.50000000","origQty":"0.00850000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"STOP_LOSS_LIMIT","side":"BUY","stopPrice":"23430.00000000","selfTradePreventionMode":"NONE"}]},{"symbol":"BTCUSDT","origClientOrderId":"4d96324ff9d44481926157","orderId":12569099453,"orderListId":-1,"clientOrderId":"91fe37ce9e69c90d6358c0","transactTime":1684804350068,"price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00001000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.23416100","status":"CANCELED","timeInForce":"GTC","type":"LIMIT","side":"SELL","stopPrice":"0.00000000","trailingDelta":0,"trailingTime":-1,"icebergQty":"0.00000000","strategyId":37463720,"strategyType":1000000,"selfTradePreventionMode":"NONE"}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::OpenOrdersCancelAllResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn open_orders_cancel_all_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OpenOrdersCancelAllParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.open_orders_cancel_all(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn open_orders_cancel_all_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OpenOrdersCancelAllParams::builder("BNBUSDT".to_string())
                    .build()
                    .unwrap();
                client.open_orders_cancel_all(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_amend_keep_priority_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderAmendKeepPriorityParams::builder("BNBUSDT".to_string(),dec!(1.0),).build().unwrap();
                client.order_amend_keep_priority(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/order.amend.keepPriority".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"56374b46-3061-486b-a311-89ee972eb648","status":200,"result":{"transactTime":1741924229819,"executionId":60,"amendedOrder":{"symbol":"BTUCSDT","orderId":23,"orderListId":4,"origClientOrderId":"my_pending_order","clientOrderId":"xbxXh5SSwaHS7oUEOCI88B","price":"1.00000000","qty":"5.00000000","executedQty":"0.00000000","preventedQty":"0.00000000","quoteOrderQty":"0.00000000","cumulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"LIMIT","side":"BUY","workingTime":1741924204920,"selfTradePreventionMode":"NONE"},"listStatus":{"orderListId":4,"contingencyType":"OTO","listOrderStatus":"EXECUTING","listClientOrderId":"8nOGLLawudj1QoOiwbroRH","symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":23,"clientOrderId":"xbxXh5SSwaHS7oUEOCI88B"},{"symbol":"BTCUSDT","orderId":22,"clientOrderId":"g04EWsjaackzedjC9wRkWD"},{"symbol":"BTCUSDT","orderId":23,"clientOrderId":"xbxXh5SSwaHS7oUEOCI88B"},{"symbol":"BTCUSDT","orderId":22,"clientOrderId":"g04EWsjaackzedjC9wRkWD"}]}},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderAmendKeepPriorityResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_amend_keep_priority_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderAmendKeepPriorityParams::builder("BNBUSDT".to_string(),dec!(1.0),).build().unwrap();
                client.order_amend_keep_priority(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_amend_keep_priority_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params =
                    OrderAmendKeepPriorityParams::builder("BNBUSDT".to_string(), dec!(1.0))
                        .build()
                        .unwrap();
                client.order_amend_keep_priority(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_cancel_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderCancelParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.order_cancel(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/order.cancel".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"16eaf097-bbec-44b9-96ff-e97e6e875870","status":200,"result":{"symbol":"BTCUSDT","origClientOrderId":"4d96324ff9d44481926157","orderId":12569099453,"orderListId":19431,"clientOrderId":"91fe37ce9e69c90d6358c0","transactTime":1684804350068,"price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00001000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.23416100","status":"CANCELED","timeInForce":"GTC","type":"LIMIT","side":"SELL","stopPrice":"0.00000000","trailingDelta":0,"icebergQty":"0.00000000","strategyId":37463720,"strategyType":1000000,"selfTradePreventionMode":"NONE","contingencyType":"OCO","listStatusType":"ALL_DONE","listOrderStatus":"ALL_DONE","listClientOrderId":"iuVNVJYYrByz6C4yGOPPK0","transactionTime":1660803702431,"orders":[{"symbol":"BTCUSDT","orderId":12569099454,"clientOrderId":"Tnu2IP0J5Y4mxw3IATBfmW"},{"symbol":"BTCUSDT","orderId":12569099453,"clientOrderId":"bX5wROblo6YeDwa9iTLeyY"},{"symbol":"BTCUSDT","orderId":12569099454,"clientOrderId":"Tnu2IP0J5Y4mxw3IATBfmW"},{"symbol":"BTCUSDT","orderId":12569099453,"clientOrderId":"bX5wROblo6YeDwa9iTLeyY"}],"orderReports":[{"symbol":"BTCUSDT","origClientOrderId":"Tnu2IP0J5Y4mxw3IATBfmW","orderId":12569099454,"orderListId":19431,"clientOrderId":"OFFXQtxVFZ6Nbcg4PgE2DA","transactTime":1684804350068,"price":"23400.00000000","origQty":"0.00850000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"LIMIT_MAKER","side":"BUY","selfTradePreventionMode":"NONE"},{"symbol":"BTCUSDT","origClientOrderId":"bX5wROblo6YeDwa9iTLeyY","orderId":12569099453,"orderListId":19431,"clientOrderId":"OFFXQtxVFZ6Nbcg4PgE2DA","transactTime":1684804350068,"price":"23450.50000000","origQty":"0.00850000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"STOP_LOSS_LIMIT","side":"BUY","stopPrice":"23430.00000000","selfTradePreventionMode":"NONE"},{"symbol":"BTCUSDT","origClientOrderId":"Tnu2IP0J5Y4mxw3IATBfmW","orderId":12569099454,"orderListId":19431,"clientOrderId":"OFFXQtxVFZ6Nbcg4PgE2DA","transactTime":1684804350068,"price":"23400.00000000","origQty":"0.00850000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"LIMIT_MAKER","side":"BUY","selfTradePreventionMode":"NONE"},{"symbol":"BTCUSDT","origClientOrderId":"bX5wROblo6YeDwa9iTLeyY","orderId":12569099453,"orderListId":19431,"clientOrderId":"OFFXQtxVFZ6Nbcg4PgE2DA","transactTime":1684804350068,"price":"23450.50000000","origQty":"0.00850000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"STOP_LOSS_LIMIT","side":"BUY","stopPrice":"23430.00000000","selfTradePreventionMode":"NONE"}]},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderCancelResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_cancel_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderCancelParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.order_cancel(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_cancel_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderCancelParams::builder("BNBUSDT".to_string())
                    .build()
                    .unwrap();
                client.order_cancel(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_cancel_replace_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderCancelReplaceParams::builder("BNBUSDT".to_string(),OrderCancelReplaceCancelReplaceModeEnum::StopOnFailure,OrderCancelReplaceSideEnum::Buy,OrderCancelReplaceTypeEnum::Market,).build().unwrap();
                client.order_cancel_replace(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/order.cancelReplace".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"99de1036-b5e2-4e0f-9b5c-13d751c93a1a","status":200,"result":{"cancelResult":"SUCCESS","newOrderResult":"SUCCESS","cancelResponse":{"symbol":"BTCUSDT","origClientOrderId":"4d96324ff9d44481926157","orderId":125690984230,"orderListId":-1,"clientOrderId":"91fe37ce9e69c90d6358c0","transactTime":1684804350068,"price":"23450.00000000","origQty":"0.00847000","executedQty":"0.00001000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.23450000","status":"CANCELED","timeInForce":"GTC","type":"LIMIT","side":"SELL","selfTradePreventionMode":"NONE"},"newOrderResponse":{"symbol":"BTCUSDT","orderId":12569099453,"orderListId":-1,"clientOrderId":"bX5wROblo6YeDwa9iTLeyY","transactTime":1660813156959,"price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"LIMIT","side":"SELL","selfTradePreventionMode":"NONE"}},"rateLimits":[{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":50,"count":1},{"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":160000,"count":1},{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderCancelReplaceResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_cancel_replace_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderCancelReplaceParams::builder("BNBUSDT".to_string(),OrderCancelReplaceCancelReplaceModeEnum::StopOnFailure,OrderCancelReplaceSideEnum::Buy,OrderCancelReplaceTypeEnum::Market,).build().unwrap();
                client.order_cancel_replace(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_cancel_replace_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderCancelReplaceParams::builder(
                    "BNBUSDT".to_string(),
                    OrderCancelReplaceCancelReplaceModeEnum::StopOnFailure,
                    OrderCancelReplaceSideEnum::Buy,
                    OrderCancelReplaceTypeEnum::Market,
                )
                .build()
                .unwrap();
                client.order_cancel_replace(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_list_cancel_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListCancelParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.order_list_cancel(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/orderList.cancel".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"c5899911-d3f4-47ae-8835-97da553d27d0","status":200,"result":{"orderListId":1274512,"contingencyType":"OCO","listStatusType":"ALL_DONE","listOrderStatus":"ALL_DONE","listClientOrderId":"6023531d7edaad348f5aff","transactionTime":1660801720215,"symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":12569138902,"clientOrderId":"jLnZpj5enfMXTuhKB1d0us"},{"symbol":"BTCUSDT","orderId":12569138901,"clientOrderId":"BqtFCj5odMoWtSqGk2X9tU"}],"orderReports":[{"symbol":"BTCUSDT","orderId":12569138902,"orderListId":1274512,"clientOrderId":"jLnZpj5enfMXTuhKB1d0us","transactTime":1660801720215,"price":"23420.00000000","origQty":"0.00650000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"LIMIT_MAKER","side":"SELL","selfTradePreventionMode":"NONE"},{"symbol":"BTCUSDT","orderId":12569138901,"orderListId":1274512,"clientOrderId":"BqtFCj5odMoWtSqGk2X9tU","transactTime":1660801720215,"price":"23410.00000000","origQty":"0.00650000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"STOP_LOSS_LIMIT","side":"SELL","stopPrice":"23405.00000000","selfTradePreventionMode":"NONE"}]},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderListCancelResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_list_cancel_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderListCancelParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.order_list_cancel(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_list_cancel_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListCancelParams::builder("BNBUSDT".to_string())
                    .build()
                    .unwrap();
                client.order_list_cancel(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_list_place_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListPlaceParams::builder("BNBUSDT".to_string(),OrderListPlaceSideEnum::Buy,dec!(1.0),dec!(1.0),).build().unwrap();
                client.order_list_place(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/orderList.place".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"57833dc0-e3f2-43fb-ba20-46480973b0aa","status":200,"result":{"orderListId":1274512,"contingencyType":"OCO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"08985fedd9ea2cf6b28996","transactionTime":1660801713793,"symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":12569138902,"clientOrderId":"jLnZpj5enfMXTuhKB1d0us"},{"symbol":"BTCUSDT","orderId":12569138901,"clientOrderId":"BqtFCj5odMoWtSqGk2X9tU"}],"orderReports":[{"symbol":"BTCUSDT","orderId":12569138902,"orderListId":1274512,"clientOrderId":"jLnZpj5enfMXTuhKB1d0us","transactTime":1660801713793,"price":"23420.00000000","origQty":"0.00650000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"LIMIT_MAKER","side":"SELL","workingTime":1660801713793,"selfTradePreventionMode":"NONE"},{"symbol":"BTCUSDT","orderId":12569138901,"orderListId":1274512,"clientOrderId":"BqtFCj5odMoWtSqGk2X9tU","transactTime":1660801713793,"price":"23410.00000000","origQty":"0.00650000","executedQty":"0.00000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"STOP_LOSS_LIMIT","side":"SELL","stopPrice":"23405.00000000","workingTime":-1,"selfTradePreventionMode":"NONE"}]},"rateLimits":[{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":50,"count":2},{"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":160000,"count":2},{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderListPlaceResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_list_place_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderListPlaceParams::builder("BNBUSDT".to_string(),OrderListPlaceSideEnum::Buy,dec!(1.0),dec!(1.0),).build().unwrap();
                client.order_list_place(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_list_place_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListPlaceParams::builder(
                    "BNBUSDT".to_string(),
                    OrderListPlaceSideEnum::Buy,
                    dec!(1.0),
                    dec!(1.0),
                )
                .build()
                .unwrap();
                client.order_list_place(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_list_place_oco_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListPlaceOcoParams::builder("BNBUSDT".to_string(),OrderListPlaceOcoSideEnum::Buy,dec!(1.0),OrderListPlaceOcoAboveTypeEnum::StopLossLimit,OrderListPlaceOcoBelowTypeEnum::StopLoss,).build().unwrap();
                client.order_list_place_oco(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/orderList.place.oco".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"56374a46-3261-486b-a211-99ed972eb648","status":200,"result":{"orderListId":2,"contingencyType":"OCO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"cKPMnDCbcLQILtDYM4f4fX","transactionTime":1711062760648,"symbol":"LTCBNB","orders":[{"symbol":"LTCBNB","orderId":3,"clientOrderId":"Z2IMlR79XNY5LU0tOxrWyW"},{"symbol":"LTCBNB","orderId":2,"clientOrderId":"0m6I4wfxvTUrOBSMUl0OPU"}],"orderReports":[{"symbol":"LTCBNB","orderId":3,"orderListId":2,"clientOrderId":"Z2IMlR79XNY5LU0tOxrWyW","transactTime":1711062760648,"price":"1.49999999","origQty":"1.000000","executedQty":"0.000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"LIMIT_MAKER","side":"BUY","workingTime":1711062760648,"selfTradePreventionMode":"NONE"},{"symbol":"LTCBNB","orderId":2,"orderListId":2,"clientOrderId":"0m6I4wfxvTUrOBSMUl0OPU","transactTime":1711062760648,"price":"1.50000000","origQty":"1.000000","executedQty":"0.000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"STOP_LOSS_LIMIT","side":"BUY","stopPrice":"1.50000001","workingTime":-1,"selfTradePreventionMode":"NONE"}]},"rateLimits":[{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":50,"count":2},{"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":160000,"count":2},{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderListPlaceOcoResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_list_place_oco_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderListPlaceOcoParams::builder("BNBUSDT".to_string(),OrderListPlaceOcoSideEnum::Buy,dec!(1.0),OrderListPlaceOcoAboveTypeEnum::StopLossLimit,OrderListPlaceOcoBelowTypeEnum::StopLoss,).build().unwrap();
                client.order_list_place_oco(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_list_place_oco_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListPlaceOcoParams::builder(
                    "BNBUSDT".to_string(),
                    OrderListPlaceOcoSideEnum::Buy,
                    dec!(1.0),
                    OrderListPlaceOcoAboveTypeEnum::StopLossLimit,
                    OrderListPlaceOcoBelowTypeEnum::StopLoss,
                )
                .build()
                .unwrap();
                client.order_list_place_oco(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_list_place_oto_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListPlaceOtoParams::builder("BNBUSDT".to_string(),OrderListPlaceOtoWorkingTypeEnum::Limit,OrderListPlaceOtoWorkingSideEnum::Buy,dec!(1.0),dec!(1.0),OrderListPlaceOtoPendingTypeEnum::Limit,OrderListPlaceOtoPendingSideEnum::Buy,dec!(1.0),).build().unwrap();
                client.order_list_place_oto(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/orderList.place.oto".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"1712544395950","status":200,"result":{"orderListId":626,"contingencyType":"OTO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"KA4EBjGnzvSwSCQsDdTrlf","transactionTime":1712544395981,"symbol":"1712544378871","orders":[{"symbol":"LTCBNB","orderId":14,"clientOrderId":"9MxJSE1TYkmyx5lbGLve7R"},{"symbol":"LTCBNB","orderId":13,"clientOrderId":"YiAUtM9yJjl1a2jXHSp9Ny"}],"orderReports":[{"symbol":"LTCBNB","orderId":14,"orderListId":626,"clientOrderId":"9MxJSE1TYkmyx5lbGLve7R","transactTime":1712544395981,"price":"0.000000","origQty":"1.000000","executedQty":"0.000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.000000","status":"PENDING_NEW","timeInForce":"GTC","type":"MARKET","side":"BUY","workingTime":-1,"selfTradePreventionMode":"NONE"},{"symbol":"LTCBNB","orderId":13,"orderListId":626,"clientOrderId":"YiAUtM9yJjl1a2jXHSp9Ny","transactTime":1712544395981,"price":"1.000000","origQty":"1.000000","executedQty":"0.000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.000000","status":"NEW","timeInForce":"GTC","type":"LIMIT","side":"SELL","workingTime":1712544395981,"selfTradePreventionMode":"NONE"}]},"rateLimits":[{"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":10000000,"count":10},{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1000,"count":38}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderListPlaceOtoResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_list_place_oto_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderListPlaceOtoParams::builder("BNBUSDT".to_string(),OrderListPlaceOtoWorkingTypeEnum::Limit,OrderListPlaceOtoWorkingSideEnum::Buy,dec!(1.0),dec!(1.0),OrderListPlaceOtoPendingTypeEnum::Limit,OrderListPlaceOtoPendingSideEnum::Buy,dec!(1.0),).build().unwrap();
                client.order_list_place_oto(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_list_place_oto_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListPlaceOtoParams::builder(
                    "BNBUSDT".to_string(),
                    OrderListPlaceOtoWorkingTypeEnum::Limit,
                    OrderListPlaceOtoWorkingSideEnum::Buy,
                    dec!(1.0),
                    dec!(1.0),
                    OrderListPlaceOtoPendingTypeEnum::Limit,
                    OrderListPlaceOtoPendingSideEnum::Buy,
                    dec!(1.0),
                )
                .build()
                .unwrap();
                client.order_list_place_oto(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_list_place_otoco_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListPlaceOtocoParams::builder("BNBUSDT".to_string(),OrderListPlaceOtocoWorkingTypeEnum::Limit,OrderListPlaceOtocoWorkingSideEnum::Buy,dec!(1.0),dec!(1.0),OrderListPlaceOtocoPendingSideEnum::Buy,dec!(1.0),OrderListPlaceOtocoPendingAboveTypeEnum::StopLossLimit,).build().unwrap();
                client.order_list_place_otoco(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/orderList.place.otoco".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"1712544408508","status":200,"result":{"orderListId":629,"contingencyType":"OTO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"GaeJHjZPasPItFj4x7Mqm6","transactionTime":1712544408537,"symbol":"1712544378871","orders":[{"symbol":"LTCBNB","orderId":25,"clientOrderId":"ilpIoShcFZ1ZGgSASKxMPt"},{"symbol":"LTCBNB","orderId":24,"clientOrderId":"YcCPKCDMQIjNvLtNswt82X"},{"symbol":"LTCBNB","orderId":23,"clientOrderId":"OVQOpKwfmPCfaBTD0n7e7H"}],"orderReports":[{"symbol":"LTCBNB","orderId":25,"orderListId":629,"clientOrderId":"ilpIoShcFZ1ZGgSASKxMPt","transactTime":1712544408537,"price":"5.000000","origQty":"5.000000","executedQty":"0.000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.000000","status":"PENDING_NEW","timeInForce":"GTC","type":"LIMIT_MAKER","side":"SELL","workingTime":-1,"selfTradePreventionMode":"NONE"},{"symbol":"LTCBNB","orderId":24,"orderListId":629,"clientOrderId":"YcCPKCDMQIjNvLtNswt82X","transactTime":1712544408537,"price":"0.000000","origQty":"5.000000","executedQty":"0.000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.000000","status":"PENDING_NEW","timeInForce":"GTC","type":"STOP_LOSS","side":"SELL","stopPrice":"0.500000","workingTime":-1,"selfTradePreventionMode":"NONE"},{"symbol":"LTCBNB","orderId":23,"orderListId":629,"clientOrderId":"OVQOpKwfmPCfaBTD0n7e7H","transactTime":1712544408537,"price":"1.500000","origQty":"1.000000","executedQty":"0.000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"0.000000","status":"NEW","timeInForce":"GTC","type":"LIMIT","side":"BUY","workingTime":1712544408537,"selfTradePreventionMode":"NONE"}]},"rateLimits":[{"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":10000000,"count":18},{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1000,"count":65}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderListPlaceOtocoResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_list_place_otoco_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderListPlaceOtocoParams::builder("BNBUSDT".to_string(),OrderListPlaceOtocoWorkingTypeEnum::Limit,OrderListPlaceOtocoWorkingSideEnum::Buy,dec!(1.0),dec!(1.0),OrderListPlaceOtocoPendingSideEnum::Buy,dec!(1.0),OrderListPlaceOtocoPendingAboveTypeEnum::StopLossLimit,).build().unwrap();
                client.order_list_place_otoco(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_list_place_otoco_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListPlaceOtocoParams::builder(
                    "BNBUSDT".to_string(),
                    OrderListPlaceOtocoWorkingTypeEnum::Limit,
                    OrderListPlaceOtocoWorkingSideEnum::Buy,
                    dec!(1.0),
                    dec!(1.0),
                    OrderListPlaceOtocoPendingSideEnum::Buy,
                    dec!(1.0),
                    OrderListPlaceOtocoPendingAboveTypeEnum::StopLossLimit,
                )
                .build()
                .unwrap();
                client.order_list_place_otoco(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_place_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderPlaceParams::builder("BNBUSDT".to_string(),OrderPlaceSideEnum::Buy,OrderPlaceTypeEnum::Market,).build().unwrap();
                client.order_place(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/order.place".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"56374a46-3061-486b-a311-99ee972eb648","status":200,"result":{"symbol":"BTCUSDT","orderId":12569099453,"orderListId":-1,"clientOrderId":"4d96324ff9d44481926157ec08158a40","transactTime":1660801715793,"price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00847000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"198.33521500","status":"FILLED","timeInForce":"GTC","type":"LIMIT","side":"SELL","workingTime":1660801715793,"selfTradePreventionMode":"NONE","fills":[{"price":"23416.50000000","qty":"0.00212000","commission":"0.000000","commissionAsset":"BNB","tradeId":1650422482},{"price":"23416.10000000","qty":"0.00635000","commission":"0.000000","commissionAsset":"BNB","tradeId":1650422481},{"price":"23416.50000000","qty":"0.00212000","commission":"0.000000","commissionAsset":"BNB","tradeId":1650422482},{"price":"23416.10000000","qty":"0.00635000","commission":"0.000000","commissionAsset":"BNB","tradeId":1650422481}]},"rateLimits":[{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":50,"count":1},{"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":160000,"count":1},{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderPlaceResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_place_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderPlaceParams::builder("BNBUSDT".to_string(),OrderPlaceSideEnum::Buy,OrderPlaceTypeEnum::Market,).build().unwrap();
                client.order_place(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_place_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderPlaceParams::builder(
                    "BNBUSDT".to_string(),
                    OrderPlaceSideEnum::Buy,
                    OrderPlaceTypeEnum::Market,
                )
                .build()
                .unwrap();
                client.order_place(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_test_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderTestParams::builder("BNBUSDT".to_string(),OrderTestSideEnum::Buy,OrderTestTypeEnum::Market,).build().unwrap();
                client.order_test(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/order.test".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"6ffebe91-01d9-43ac-be99-57cf062e0e30","status":200,"result":{"standardCommissionForOrder":{"maker":"0.00000112","taker":"0.00000114"},"specialCommissionForOrder":{"maker":"0.05000000","taker":"0.06000000"},"taxCommissionForOrder":{"maker":"0.00000112","taker":"0.00000114"},"discount":{"enabledForAccount":true,"enabledForSymbol":true,"discountAsset":"BNB","discount":"0.25000000"}},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderTestResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_test_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderTestParams::builder("BNBUSDT".to_string(),OrderTestSideEnum::Buy,OrderTestTypeEnum::Market,).build().unwrap();
                client.order_test(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_test_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderTestParams::builder(
                    "BNBUSDT".to_string(),
                    OrderTestSideEnum::Buy,
                    OrderTestTypeEnum::Market,
                )
                .build()
                .unwrap();
                client.order_test(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn sor_order_place_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = SorOrderPlaceParams::builder("BNBUSDT".to_string(),SorOrderPlaceSideEnum::Buy,SorOrderPlaceTypeEnum::Market,dec!(1.0),).build().unwrap();
                client.sor_order_place(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/sor.order.place".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"3a4437e2-41a3-4c19-897c-9cadc5dce8b6","status":200,"result":[{"symbol":"BTCUSDT","orderId":2,"orderListId":-1,"clientOrderId":"sBI1KM6nNtOfj5tccZSKly","transactTime":1689149087774,"price":"31000.00000000","origQty":"0.50000000","executedQty":"0.50000000","origQuoteOrderQty":"0.000000","cummulativeQuoteQty":"14000.00000000","status":"FILLED","timeInForce":"GTC","type":"LIMIT","side":"BUY","workingTime":1689149087774,"fills":[{"matchType":"ONE_PARTY_TRADE_REPORT","price":"28000.00000000","qty":"0.50000000","commission":"0.00000000","commissionAsset":"BTC","tradeId":-1,"allocId":0}],"workingFloor":"SOR","selfTradePreventionMode":"NONE","usedSor":true}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::SorOrderPlaceResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn sor_order_place_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = SorOrderPlaceParams::builder("BNBUSDT".to_string(),SorOrderPlaceSideEnum::Buy,SorOrderPlaceTypeEnum::Market,dec!(1.0),).build().unwrap();
                client.sor_order_place(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn sor_order_place_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = SorOrderPlaceParams::builder(
                    "BNBUSDT".to_string(),
                    SorOrderPlaceSideEnum::Buy,
                    SorOrderPlaceTypeEnum::Market,
                    dec!(1.0),
                )
                .build()
                .unwrap();
                client.sor_order_place(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn sor_order_test_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = SorOrderTestParams::builder("BNBUSDT".to_string(),SorOrderTestSideEnum::Buy,SorOrderTestTypeEnum::Market,dec!(1.0),).build().unwrap();
                client.sor_order_test(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/sor.order.test".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"3a4437e2-41a3-4c19-897c-9cadc5dce8b6","status":200,"result":{"standardCommissionForOrder":{"maker":"0.00000112","taker":"0.00000114"},"taxCommissionForOrder":{"maker":"0.00000112","taker":"0.00000114"},"discount":{"enabledForAccount":true,"enabledForSymbol":true,"discountAsset":"BNB","discount":"0.25"}},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":1}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::SorOrderTestResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn sor_order_test_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = SorOrderTestParams::builder("BNBUSDT".to_string(),SorOrderTestSideEnum::Buy,SorOrderTestTypeEnum::Market,dec!(1.0),).build().unwrap();
                client.sor_order_test(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn sor_order_test_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = TradeApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = SorOrderTestParams::builder(
                    "BNBUSDT".to_string(),
                    SorOrderTestSideEnum::Buy,
                    SorOrderTestTypeEnum::Market,
                    dec!(1.0),
                )
                .build()
                .unwrap();
                client.sor_order_test(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }
}
